summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/CODEOWNERS292
-rw-r--r--.github/workflows/runner.yml2
-rw-r--r--SConstruct75
-rw-r--r--core/core_constants.cpp1
-rw-r--r--core/extension/extension_api_dump.cpp1
-rw-r--r--core/extension/make_wrappers.py4
-rw-r--r--core/math/vector4.h6
-rw-r--r--core/object/make_virtuals.py30
-rw-r--r--core/object/object.h9
-rw-r--r--core/object/script_language_extension.h132
-rw-r--r--core/string/ustring.cpp25
-rw-r--r--core/string/ustring.h2
-rw-r--r--core/variant/variant_call.cpp31
-rw-r--r--doc/classes/@GlobalScope.xml10
-rw-r--r--doc/classes/Animation.xml80
-rw-r--r--doc/classes/AnimationPlayer.xml89
-rw-r--r--doc/classes/Array.xml15
-rw-r--r--doc/classes/BaseButton.xml1
-rw-r--r--doc/classes/CameraFeed.xml28
-rw-r--r--doc/classes/Control.xml4
-rw-r--r--doc/classes/Dictionary.xml8
-rw-r--r--doc/classes/GraphEdit.xml5
-rw-r--r--doc/classes/HTTPClient.xml2
-rw-r--r--doc/classes/ItemList.xml13
-rw-r--r--doc/classes/LineEdit.xml8
-rw-r--r--doc/classes/PackedByteArray.xml7
-rw-r--r--doc/classes/PackedColorArray.xml7
-rw-r--r--doc/classes/PackedFloat32Array.xml7
-rw-r--r--doc/classes/PackedFloat64Array.xml7
-rw-r--r--doc/classes/PackedInt32Array.xml7
-rw-r--r--doc/classes/PackedInt64Array.xml7
-rw-r--r--doc/classes/PackedStringArray.xml7
-rw-r--r--doc/classes/PackedVector2Array.xml7
-rw-r--r--doc/classes/PackedVector3Array.xml7
-rw-r--r--doc/classes/PackedVector4Array.xml7
-rw-r--r--doc/classes/ParticleProcessMaterial.xml3
-rw-r--r--doc/classes/ProjectSettings.xml5
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.cpp3
-rw-r--r--drivers/gles3/shaders/scene.glsl289
-rw-r--r--drivers/gles3/storage/config.cpp2
-rw-r--r--drivers/gles3/storage/material_storage.cpp4
-rw-r--r--editor/animation_track_editor.cpp1398
-rw-r--r--editor/animation_track_editor.h216
-rw-r--r--editor/debugger/editor_debugger_server.cpp4
-rw-r--r--editor/editor_file_system.cpp5
-rw-r--r--editor/editor_file_system.h12
-rw-r--r--editor/editor_node.cpp1
-rw-r--r--editor/editor_script.cpp2
-rw-r--r--editor/editor_script.h2
-rw-r--r--editor/editor_vcs_interface.cpp46
-rw-r--r--editor/editor_vcs_interface.h46
-rw-r--r--editor/export/editor_export_platform_extension.cpp18
-rw-r--r--editor/export/editor_export_platform_extension.h18
-rw-r--r--editor/export/editor_export_plugin.cpp8
-rw-r--r--editor/export/editor_export_plugin.h8
-rw-r--r--editor/file_info.cpp (renamed from modules/squish/image_decompress_squish.cpp)79
-rw-r--r--editor/file_info.h74
-rw-r--r--editor/filesystem_dock.cpp159
-rw-r--r--editor/filesystem_dock.h32
-rw-r--r--editor/gui/editor_dir_dialog.cpp3
-rw-r--r--editor/gui/editor_file_dialog.cpp138
-rw-r--r--editor/gui/editor_file_dialog.h16
-rw-r--r--editor/icons/Marker.svg1
-rw-r--r--editor/icons/MarkerSelected.svg1
-rw-r--r--editor/plugins/animation_blend_tree_editor_plugin.cpp166
-rw-r--r--editor/plugins/animation_blend_tree_editor_plugin.h47
-rw-r--r--editor/plugins/animation_player_editor_plugin.cpp58
-rw-r--r--editor/plugins/animation_player_editor_plugin.h26
-rw-r--r--editor/plugins/asset_library_editor_plugin.cpp3
-rw-r--r--editor/plugins/tool_button_editor_plugin.cpp82
-rw-r--r--editor/plugins/tool_button_editor_plugin.h57
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp1
-rw-r--r--editor/project_manager/project_dialog.cpp6
-rw-r--r--editor/register_editor_types.cpp2
-rw-r--r--editor/renames_map_3_to_4.cpp2
-rw-r--r--editor/scene_tree_dock.cpp49
-rw-r--r--methods.py36
-rw-r--r--modules/bcdec/SCsub10
-rw-r--r--modules/bcdec/config.py (renamed from modules/squish/config.py)0
-rw-r--r--modules/bcdec/image_decompress_bcdec.cpp181
-rw-r--r--modules/bcdec/image_decompress_bcdec.h (renamed from modules/squish/image_decompress_squish.h)21
-rw-r--r--modules/bcdec/register_types.cpp (renamed from modules/squish/register_types.cpp)9
-rw-r--r--modules/bcdec/register_types.h (renamed from modules/squish/register_types.h)10
-rw-r--r--modules/camera/buffer_decoder.cpp10
-rw-r--r--modules/camera/camera_feed_linux.cpp10
-rw-r--r--modules/camera/camera_macos.mm2
-rw-r--r--modules/cvtt/register_types.cpp1
-rw-r--r--modules/gdscript/doc_classes/@GDScript.xml35
-rw-r--r--modules/gdscript/gdscript_parser.cpp63
-rw-r--r--modules/gdscript/gdscript_parser.h1
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/export_tool_button_requires_tool_mode.gd1
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/export_tool_button_requires_tool_mode.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/features/export_variable.gd5
-rw-r--r--modules/gdscript/tests/scripts/parser/features/export_variable.out4
-rw-r--r--modules/gdscript/tests/scripts/utils.notest.gd3
-rw-r--r--modules/mbedtls/SCsub16
-rw-r--r--modules/mbedtls/crypto_mbedtls.cpp4
-rw-r--r--modules/mbedtls/register_types.cpp27
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/EventSignals_ScriptSignals.generated.cs4
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs26
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs46
-rw-r--r--modules/mono/editor/bindings_generator.cpp40
-rw-r--r--modules/multiplayer/doc_classes/MultiplayerSpawner.xml4
-rw-r--r--modules/squish/SCsub45
-rw-r--r--modules/vorbis/resource_importer_ogg_vorbis.cpp1
-rw-r--r--modules/websocket/editor/editor_debugger_server_websocket.cpp4
-rw-r--r--modules/websocket/emws_peer.cpp3
-rw-r--r--modules/websocket/wsl_peer.cpp3
-rw-r--r--platform/android/doc_classes/EditorExportPlatformAndroid.xml3
-rw-r--r--platform/android/export/export_plugin.cpp42
-rw-r--r--platform/android/export/export_plugin.h5
-rw-r--r--platform/android/java/editor/src/horizonos/java/org/godotengine/editor/GodotEditor.kt4
-rw-r--r--platform/android/java/editor/src/horizonos/java/org/godotengine/editor/GodotXRGame.kt2
-rw-r--r--platform/android/java/lib/res/mipmap-anydpi-v26/icon.xml1
-rw-r--r--platform/android/java/lib/res/mipmap/icon_monochrome.pngbin0 -> 5617 bytes
-rw-r--r--platform/linuxbsd/detect.py4
-rw-r--r--platform/web/detect.py29
-rw-r--r--scene/3d/mesh_instance_3d.cpp4
-rw-r--r--scene/animation/animation_blend_tree.cpp2
-rw-r--r--scene/animation/animation_mixer.cpp93
-rw-r--r--scene/animation/animation_mixer.h2
-rw-r--r--scene/animation/animation_player.cpp187
-rw-r--r--scene/animation/animation_player.h16
-rw-r--r--scene/gui/base_button.cpp34
-rw-r--r--scene/gui/base_button.h2
-rw-r--r--scene/gui/button.cpp5
-rw-r--r--scene/gui/control.cpp17
-rw-r--r--scene/gui/control.h5
-rw-r--r--scene/gui/graph_edit.cpp4
-rw-r--r--scene/main/http_request.cpp3
-rw-r--r--scene/main/viewport.cpp3
-rw-r--r--scene/resources/animation.cpp147
-rw-r--r--scene/resources/animation.h27
-rw-r--r--scene/resources/gradient_texture.cpp12
-rw-r--r--scene/resources/gradient_texture.h12
-rw-r--r--scene/resources/material.cpp4
-rw-r--r--scene/resources/material.h4
-rw-r--r--scene/resources/mesh.cpp28
-rw-r--r--scene/resources/mesh.h28
-rw-r--r--scene/resources/style_box.cpp2
-rw-r--r--scene/resources/style_box.h2
-rw-r--r--scene/resources/syntax_highlighter.cpp2
-rw-r--r--scene/resources/texture.cpp30
-rw-r--r--scene/resources/texture.h30
-rw-r--r--scene/resources/video_stream.cpp2
-rw-r--r--scene/resources/video_stream.h2
-rw-r--r--scene/scene_string_names.cpp1
-rw-r--r--scene/scene_string_names.h1
-rw-r--r--servers/audio/audio_effect.cpp4
-rw-r--r--servers/audio/audio_effect.h4
-rw-r--r--servers/audio/audio_stream.cpp6
-rw-r--r--servers/audio/audio_stream.h6
-rw-r--r--servers/camera/camera_feed.cpp17
-rw-r--r--servers/camera/camera_feed.h6
-rw-r--r--servers/extensions/physics_server_2d_extension.h44
-rw-r--r--servers/extensions/physics_server_3d_extension.h44
-rw-r--r--servers/movie_writer/movie_writer.cpp14
-rw-r--r--servers/movie_writer/movie_writer.h14
-rw-r--r--servers/physics_server_3d.cpp6
-rw-r--r--servers/physics_server_3d.h6
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp5
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp9
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp5
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp7
-rw-r--r--servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp164
-rw-r--r--servers/rendering/renderer_rd/renderer_canvas_render_rd.h43
-rw-r--r--servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl10
-rw-r--r--servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl190
-rw-r--r--servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl326
-rw-r--r--servers/rendering/renderer_rd/shaders/scene_forward_vertex_lights_inc.glsl82
-rw-r--r--servers/rendering/renderer_rd/storage_rd/texture_storage.cpp97
-rw-r--r--servers/rendering/renderer_rd/storage_rd/texture_storage.h27
-rw-r--r--servers/rendering_server.cpp3
-rw-r--r--servers/text/text_server_extension.cpp160
-rw-r--r--servers/text/text_server_extension.h160
-rw-r--r--tests/core/string/test_string.h40
-rw-r--r--thirdparty/README.md23
-rw-r--r--thirdparty/mbedtls/include/godot_module_mbedtls_config.h12
-rw-r--r--thirdparty/mbedtls/library/psa_crypto.c9233
-rw-r--r--thirdparty/mbedtls/library/psa_crypto_aead.c649
-rw-r--r--thirdparty/mbedtls/library/psa_crypto_cipher.c724
-rw-r--r--thirdparty/mbedtls/library/psa_crypto_client.c22
-rw-r--r--thirdparty/mbedtls/library/psa_crypto_driver_wrappers_no_static.c256
-rw-r--r--thirdparty/mbedtls/library/psa_crypto_ecp.c596
-rw-r--r--thirdparty/mbedtls/library/psa_crypto_ffdh.c321
-rw-r--r--thirdparty/mbedtls/library/psa_crypto_hash.c470
-rw-r--r--thirdparty/mbedtls/library/psa_crypto_mac.c496
-rw-r--r--thirdparty/mbedtls/library/psa_crypto_pake.c571
-rw-r--r--thirdparty/mbedtls/library/psa_crypto_rsa.c705
-rw-r--r--thirdparty/mbedtls/library/psa_crypto_se.c373
-rw-r--r--thirdparty/mbedtls/library/psa_crypto_slot_management.c1131
-rw-r--r--thirdparty/mbedtls/library/psa_crypto_storage.c481
-rw-r--r--thirdparty/mbedtls/library/psa_its_file.c254
-rw-r--r--thirdparty/mbedtls/library/psa_util.c608
-rw-r--r--thirdparty/misc/bcdec.h1329
-rw-r--r--thirdparty/openxr/COPYING.adoc4
-rw-r--r--thirdparty/openxr/include/openxr/openxr.h644
-rw-r--r--thirdparty/openxr/include/openxr/openxr_platform.h37
-rw-r--r--thirdparty/openxr/include/openxr/openxr_reflection.h549
-rw-r--r--thirdparty/openxr/include/openxr/openxr_reflection_parent_structs.h60
-rw-r--r--thirdparty/openxr/include/openxr/openxr_reflection_structs.h49
-rw-r--r--thirdparty/openxr/src/common/platform_utils.hpp5
-rw-r--r--thirdparty/openxr/src/common/xr_linear.h2
-rw-r--r--thirdparty/openxr/src/loader/android_utilities.cpp54
-rw-r--r--thirdparty/openxr/src/loader/loader_core.cpp16
-rw-r--r--thirdparty/openxr/src/loader/loader_instance.cpp4
-rw-r--r--thirdparty/openxr/src/loader/loader_instance.hpp6
-rw-r--r--thirdparty/openxr/src/loader/manifest_file.cpp27
-rw-r--r--thirdparty/openxr/src/loader/runtime_interface.cpp14
-rw-r--r--thirdparty/openxr/src/loader/runtime_interface.hpp8
-rw-r--r--thirdparty/openxr/src/loader/xr_generated_loader.cpp13
-rw-r--r--thirdparty/openxr/src/loader/xr_generated_loader.hpp4
-rw-r--r--thirdparty/openxr/src/xr_generated_dispatch_table_core.c5
-rw-r--r--thirdparty/openxr/src/xr_generated_dispatch_table_core.h7
-rw-r--r--thirdparty/squish/LICENSE.txt20
-rw-r--r--thirdparty/squish/alpha.cpp350
-rw-r--r--thirdparty/squish/alpha.h41
-rw-r--r--thirdparty/squish/clusterfit.cpp392
-rw-r--r--thirdparty/squish/clusterfit.h61
-rw-r--r--thirdparty/squish/colourblock.cpp247
-rw-r--r--thirdparty/squish/colourblock.h45
-rw-r--r--thirdparty/squish/colourfit.cpp54
-rw-r--r--thirdparty/squish/colourfit.h56
-rw-r--r--thirdparty/squish/colourset.cpp121
-rw-r--r--thirdparty/squish/colourset.h58
-rw-r--r--thirdparty/squish/config.h69
-rw-r--r--thirdparty/squish/maths.cpp259
-rw-r--r--thirdparty/squish/maths.h233
-rw-r--r--thirdparty/squish/patches/config_sse.patch31
-rw-r--r--thirdparty/squish/patches/decompress_bc4_bc5.patch85
-rw-r--r--thirdparty/squish/rangefit.cpp201
-rw-r--r--thirdparty/squish/rangefit.h54
-rw-r--r--thirdparty/squish/simd.h40
-rw-r--r--thirdparty/squish/simd_float.h183
-rw-r--r--thirdparty/squish/simd_sse.h180
-rw-r--r--thirdparty/squish/simd_ve.h166
-rw-r--r--thirdparty/squish/singlecolourfit.cpp172
-rw-r--r--thirdparty/squish/singlecolourfit.h58
-rw-r--r--thirdparty/squish/singlecolourlookup.inl1064
-rw-r--r--thirdparty/squish/squish.cpp411
-rw-r--r--thirdparty/squish/squish.h309
241 files changed, 25080 insertions, 6604 deletions
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index a1d406ff92..748d787b86 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -4,180 +4,234 @@
# Buildsystem
-.* @godotengine/buildsystem
-.github/ @godotengine/buildsystem
-*.py @godotengine/buildsystem
-SConstruct @godotengine/buildsystem
-SCsub @godotengine/buildsystem
+* @godotengine/buildsystem
# Core
-/core/ @godotengine/core
-/core/crypto/ @godotengine/network
-/core/debugger/ @godotengine/debugger
-/core/extension/ @godotengine/gdextension
-/core/input/ @godotengine/input
+/core/ @godotengine/core
+/core/crypto/ @godotengine/network
+/core/debugger/ @godotengine/debugger
+/core/extension/ @godotengine/gdextension
+/core/input/ @godotengine/input
# Doc
-/doc/ @godotengine/documentation
-doc_classes/* @godotengine/documentation
+/doc/ @godotengine/documentation
+**/doc_classes/ @godotengine/documentation
# Drivers
## Audio
-/drivers/alsa/ @godotengine/audio
-/drivers/alsamidi/ @godotengine/audio
-/drivers/coreaudio/ @godotengine/audio
-/drivers/coremidi/ @godotengine/audio
-/drivers/pulseaudio/ @godotengine/audio
-/drivers/wasapi/ @godotengine/audio
-/drivers/winmidi/ @godotengine/audio
-/drivers/xaudio2/ @godotengine/audio
+/drivers/alsa/ @godotengine/audio
+/drivers/alsamidi/ @godotengine/audio
+/drivers/coreaudio/ @godotengine/audio
+/drivers/coremidi/ @godotengine/audio
+/drivers/pulseaudio/ @godotengine/audio
+/drivers/wasapi/ @godotengine/audio
+/drivers/winmidi/ @godotengine/audio
+/drivers/xaudio2/ @godotengine/audio
## Rendering
-/drivers/d3d12/ @godotengine/rendering
-/drivers/dummy/ @godotengine/rendering
-/drivers/gles3/ @godotengine/rendering
-/drivers/spirv-reflect/ @godotengine/rendering
-/drivers/vulkan/ @godotengine/rendering
+/drivers/d3d12/ @godotengine/rendering
+/drivers/dummy/ @godotengine/rendering
+/drivers/gles3/ @godotengine/rendering
+/drivers/spirv-reflect/ @godotengine/rendering
+/drivers/vulkan/ @godotengine/rendering
## OS
-/drivers/unix/ @godotengine/_platforms
-/drivers/windows/ @godotengine/windows
+/drivers/unix/ @godotengine/_platforms
+/drivers/windows/ @godotengine/windows
## Misc
-/drivers/png/ @godotengine/import
+/drivers/png/ @godotengine/import
# Editor
-/editor/*debugger* @godotengine/debugger
-/editor/gui/ @godotengine/usability @godotengine/gui-nodes
-/editor/icons/ @godotengine/usability
-/editor/import/ @godotengine/import
-/editor/plugins/*2d_*.* @godotengine/2d-editor
-/editor/plugins/*3d_*.* @godotengine/3d-editor
-/editor/plugins/script_*.* @godotengine/script-editor
-/editor/plugins/*shader*.* @godotengine/shaders
-/editor/themes/ @godotengine/usability @godotengine/gui-nodes
-/editor/code_editor.* @godotengine/script-editor
-/editor/*dock*.* @godotengine/docks
-/editor/*shader*.* @godotengine/shaders
+/editor/*debugger* @godotengine/debugger
+/editor/gui/ @godotengine/usability @godotengine/gui-nodes
+/editor/icons/ @godotengine/usability
+/editor/import/ @godotengine/import
+/editor/plugins/*2d_*.* @godotengine/2d-editor
+/editor/plugins/*3d_*.* @godotengine/3d-editor
+/editor/plugins/script_*.* @godotengine/script-editor
+/editor/plugins/*shader*.* @godotengine/shaders
+/editor/themes/ @godotengine/usability @godotengine/gui-nodes
+/editor/code_editor.* @godotengine/script-editor
+/editor/*dock*.* @godotengine/docks
+/editor/*shader*.* @godotengine/shaders
# Main
-/main/ @godotengine/core
+/main/ @godotengine/core
# Misc
-/misc/ @godotengine/buildsystem
-/misc/extension_api_validation/ @godotengine/gdextension @godotengine/dotnet
+/misc/ @godotengine/buildsystem
+/misc/extension_api_validation/ @godotengine/gdextension @godotengine/dotnet
# Modules
## Audio (+ video)
-/modules/minimp3/ @godotengine/audio
-/modules/ogg/ @godotengine/audio
-/modules/opus/ @godotengine/audio
-/modules/theora/ @godotengine/audio
-/modules/vorbis/ @godotengine/audio
-/modules/webm/ @godotengine/audio
+/modules/interactive_music/ @godotengine/audio
+/modules/interactive_music/doc_classes/ @godotengine/audio @godotengine/documentation
+/modules/minimp3/ @godotengine/audio
+/modules/minimp3/doc_classes/ @godotengine/audio @godotengine/documentation
+/modules/ogg/ @godotengine/audio
+/modules/ogg/doc_classes/ @godotengine/audio @godotengine/documentation
+/modules/theora/ @godotengine/audio
+/modules/theora/doc_classes/ @godotengine/audio @godotengine/documentation
+/modules/vorbis/ @godotengine/audio
+/modules/vorbis/doc_classes/ @godotengine/audio @godotengine/documentation
## Import
-/modules/basis_universal/ @godotengine/import
-/modules/bmp/ @godotengine/import
-/modules/cvtt/ @godotengine/import
-/modules/dds/ @godotengine/import
-/modules/etc/ @godotengine/import
-/modules/fbx/ @godotengine/import
-/modules/gltf/ @godotengine/import
-/modules/hdr/ @godotengine/import
-/modules/jpg/ @godotengine/import
-/modules/pvr/ @godotengine/import
-/modules/squish/ @godotengine/import
-/modules/svg/ @godotengine/import
-/modules/tga/ @godotengine/import
-/modules/tinyexr/ @godotengine/import
-/modules/webp/ @godotengine/import
+/modules/astcenc/ @godotengine/import
+/modules/basis_universal/ @godotengine/import
+/modules/betsy/ @godotengine/import
+/modules/bmp/ @godotengine/import
+/modules/cvtt/ @godotengine/import
+/modules/dds/ @godotengine/import
+/modules/etcpak/ @godotengine/import
+/modules/fbx/ @godotengine/import
+/modules/fbx/doc_classes/ @godotengine/import @godotengine/documentation
+/modules/gltf/ @godotengine/import
+/modules/gltf/doc_classes/ @godotengine/import @godotengine/documentation
+/modules/gltf/tests/ @godotengine/import @godotengine/tests
+/modules/hdr/ @godotengine/import
+/modules/jpg/ @godotengine/import
+/modules/ktx/ @godotengine/import
+/modules/squish/ @godotengine/import
+/modules/svg/ @godotengine/import
+/modules/tga/ @godotengine/import
+/modules/tinyexr/ @godotengine/import
+/modules/webp/ @godotengine/import
## Network
-/modules/enet/ @godotengine/network
-/modules/mbedtls/ @godotengine/network
-/modules/upnp/ @godotengine/network
-/modules/webrtc/ @godotengine/network
-/modules/websocket/ @godotengine/network
+/modules/enet/ @godotengine/network
+/modules/enet/doc_classes/ @godotengine/network @godotengine/documentation
+/modules/mbedtls/ @godotengine/network
+/modules/mbedtls/tests/ @godotengine/network @godotengine/tests
+/modules/multiplayer/ @godotengine/network
+/modules/multiplayer/doc_classes/ @godotengine/network @godotengine/documentation
+/modules/upnp/ @godotengine/network
+/modules/upnp/doc_classes/ @godotengine/network @godotengine/documentation
+/modules/webrtc/ @godotengine/network
+/modules/webrtc/doc_classes/ @godotengine/network @godotengine/documentation
+/modules/websocket/ @godotengine/network
+/modules/websocket/doc_classes/ @godotengine/network @godotengine/documentation
+
+## Physics
+/modules/godot_physics_2d/ @godotengine/physics
+/modules/godot_physics_3d/ @godotengine/physics
## Rendering
-/modules/denoise/ @godotengine/rendering
-/modules/glslang/ @godotengine/rendering
-/modules/lightmapper_rd/ @godotengine/rendering
-/modules/meshoptimizer/ @godotengine/rendering
-/modules/vhacd/ @godotengine/rendering
-/modules/xatlas_unwrap/ @godotengine/rendering
+/modules/glslang/ @godotengine/rendering
+/modules/lightmapper_rd/ @godotengine/rendering
+/modules/meshoptimizer/ @godotengine/rendering
+/modules/raycast/ @godotengine/rendering
+/modules/vhacd/ @godotengine/rendering
+/modules/xatlas_unwrap/ @godotengine/rendering
## Scripting
-/modules/gdscript/ @godotengine/gdscript
-/modules/jsonrpc/ @godotengine/gdscript
-/modules/mono/ @godotengine/dotnet
+/modules/gdscript/ @godotengine/gdscript
+/modules/gdscript/doc_classes/ @godotengine/gdscript @godotengine/documentation
+/modules/gdscript/icons/ @godotengine/gdscript @godotengine/usability
+/modules/gdscript/tests/ @godotengine/gdscript @godotengine/tests
+/modules/jsonrpc/ @godotengine/gdscript @godotengine/network
+/modules/jsonrpc/tests @godotengine/gdscript @godotengine/network @godotengine/tests
+/modules/mono/ @godotengine/dotnet
+/modules/mono/doc_classes/ @godotengine/dotnet @godotengine/documentation
+/modules/mono/icons/ @godotengine/dotnet @godotengine/usability
## Text
-/modules/freetype/ @godotengine/buildsystem
-/modules/text_server_adv/ @godotengine/gui-nodes
-/modules/text_server_fb/ @godotengine/gui-nodes
+/modules/freetype/ @godotengine/buildsystem
+/modules/msdfgen/ @godotengine/buildsystem
+/modules/text_server_adv/ @godotengine/gui-nodes
+/modules/text_server_adv/doc_classes/ @godotengine/gui-nodes @godotengine/documentation
+/modules/text_server_fb/ @godotengine/gui-nodes
+/modules/text_server_fb/doc_classes/ @godotengine/gui-nodes @godotengine/documentation
## XR
-/modules/camera/ @godotengine/xr
-/modules/gdextension/xr/ @godotengine/xr
-/modules/mobile_vr/ @godotengine/xr
-/modules/webxr/ @godotengine/xr
+/modules/camera/ @godotengine/xr
+/modules/mobile_vr/ @godotengine/xr
+/modules/mobile_vr/doc_classes/ @godotengine/xr @godotengine/documentation
+/modules/openxr/ @godotengine/xr
+/modules/openxr/doc_classes/ @godotengine/xr @godotengine/documentation
+/modules/webxr/ @godotengine/xr
+/modules/webxr/doc_classes/ @godotengine/xr @godotengine/documentation
## Misc
-/modules/bullet/ @godotengine/physics
-/modules/csg/ @godotengine/3d-nodes
-/modules/gdnavigation/ @godotengine/navigation
-/modules/gridmap/ @godotengine/3d-nodes
-/modules/opensimplex/ @godotengine/3d-nodes
-/modules/regex/ @godotengine/core
+/modules/csg/ @godotengine/3d-nodes
+/modules/csg/doc_classes/ @godotengine/3d-nodes @godotengine/documentation
+/modules/csg/icons/ @godotengine/3d-nodes @godotengine/usability
+/modules/navigation/ @godotengine/navigation
+/modules/gridmap/ @godotengine/3d-nodes
+/modules/gridmap/doc_classes/ @godotengine/3d-nodes @godotengine/documentation
+/modules/gridmap/icons/ @godotengine/3d-nodes @godotengine/usability
+/modules/noise/ @godotengine/core
+/modules/noise/doc_classes/ @godotengine/core @godotengine/documentation
+/modules/noise/icons/ @godotengine/core @godotengine/usability
+/modules/noise/tests/ @godotengine/core @godotengine/tests
+/modules/regex/ @godotengine/core
+/modules/regex/doc_classes/ @godotengine/core @godotengine/documentation
+/modules/regex/icons/ @godotengine/core @godotengine/usability
+/modules/regex/tests/ @godotengine/core @godotengine/tests
+/modules/zip/ @godotengine/core
+/modules/zip/doc_classes/ @godotengine/core @godotengine/documentation
# Platform
-/platform/android/ @godotengine/android
-/platform/ios/ @godotengine/ios
-/platform/linuxbsd/ @godotengine/linux-bsd
-/platform/macos/ @godotengine/macos
-/platform/web/ @godotengine/web
-/platform/windows/ @godotengine/windows
+/platform/android/ @godotengine/android
+/platform/android/doc_classes/ @godotengine/android @godotengine/documentation
+/platform/ios/ @godotengine/ios
+/platform/ios/doc_classes/ @godotengine/ios @godotengine/documentation
+/platform/linuxbsd/ @godotengine/linux-bsd
+/platform/linuxbsd/doc_classes/ @godotengine/linux-bsd @godotengine/documentation
+/platform/macos/ @godotengine/macos
+/platform/macos/doc_classes/ @godotengine/macos @godotengine/documentation
+/platform/web/ @godotengine/web
+/platform/web/doc_classes/ @godotengine/web @godotengine/documentation
+/platform/windows/ @godotengine/windows
+/platform/windows/doc_classes/ @godotengine/windows @godotengine/documentation
# Scene
-/scene/2d/ @godotengine/2d-nodes
-/scene/3d/ @godotengine/3d-nodes
-/scene/animation/ @godotengine/animation
-/scene/audio/ @godotengine/audio
-/scene/debugger/ @godotengine/debugger
-/scene/gui/ @godotengine/gui-nodes
-/scene/main/ @godotengine/core
-/scene/resources/font.* @godotengine/gui-nodes
-/scene/resources/text_line.* @godotengine/gui-nodes
-/scene/resources/text_paragraph.* @godotengine/gui-nodes
-/scene/resources/visual_shader*.* @godotengine/shaders
-/scene/theme/ @godotengine/gui-nodes
+/scene/2d/ @godotengine/2d-nodes
+/scene/2d/physics/ @godotengine/2d-nodes @godotengine/physics
+/scene/3d/ @godotengine/3d-nodes
+/scene/3d/physics/ @godotengine/3d-nodes @godotengine/physics
+/scene/animation/ @godotengine/animation
+/scene/audio/ @godotengine/audio
+/scene/debugger/ @godotengine/debugger
+/scene/gui/ @godotengine/gui-nodes
+/scene/main/ @godotengine/core
+/scene/resources/font.* @godotengine/gui-nodes
+/scene/resources/text_line.* @godotengine/gui-nodes
+/scene/resources/text_paragraph.* @godotengine/gui-nodes
+/scene/resources/visual_shader*.* @godotengine/shaders
+/scene/theme/ @godotengine/gui-nodes
+/scene/theme/icons/ @godotengine/gui-nodes @godotengine/usability
# Servers
-/servers/audio* @godotengine/audio
-/servers/camera* @godotengine/xr
-/servers/display_server.* @godotengine/_platforms
-/servers/navigation_server*.* @godotengine/navigation
-/servers/physics* @godotengine/physics
-/servers/rendering* @godotengine/rendering
-/servers/text_server.* @godotengine/gui-nodes
-/servers/xr* @godotengine/xr
+/servers/audio* @godotengine/audio
+/servers/camera* @godotengine/xr
+/servers/display_server.* @godotengine/_platforms
+/servers/navigation_server*.* @godotengine/navigation
+/servers/physics* @godotengine/physics
+/servers/rendering* @godotengine/rendering
+/servers/text_server.* @godotengine/gui-nodes
+/servers/xr* @godotengine/xr
# Tests
-/tests/ @godotengine/tests
+/tests/ @godotengine/tests
# Thirdparty
-/thirdparty/ @godotengine/buildsystem
+/thirdparty/ @godotengine/buildsystem
+
+# Buildsystem (After everything to catch all)
+
+*.py @godotengine/buildsystem
+SConstruct @godotengine/buildsystem
+SCsub @godotengine/buildsystem
diff --git a/.github/workflows/runner.yml b/.github/workflows/runner.yml
index d2d0e3571f..fd5e74b914 100644
--- a/.github/workflows/runner.yml
+++ b/.github/workflows/runner.yml
@@ -1,5 +1,5 @@
name: 🔗 GHA
-on: [push, pull_request]
+on: [push, pull_request, merge_group]
concurrency:
group: ci-${{ github.actor }}-${{ github.head_ref || github.run_number }}-${{ github.ref }}-runner
diff --git a/SConstruct b/SConstruct
index 5566770148..63cff4fe16 100644
--- a/SConstruct
+++ b/SConstruct
@@ -299,7 +299,6 @@ opts.Add(BoolVariable("builtin_pcre2_with_jit", "Use JIT compiler for the built-
opts.Add(BoolVariable("builtin_recastnavigation", "Use the built-in Recast navigation library", True))
opts.Add(BoolVariable("builtin_rvo2_2d", "Use the built-in RVO2 2D library", True))
opts.Add(BoolVariable("builtin_rvo2_3d", "Use the built-in RVO2 3D library", True))
-opts.Add(BoolVariable("builtin_squish", "Use the built-in squish library", True))
opts.Add(BoolVariable("builtin_xatlas", "Use the built-in xatlas 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))
@@ -640,25 +639,18 @@ if env.dev_build:
print("NOTE: Developer build, with debug optimization level and debug symbols (unless overridden).")
# Enforce our minimal compiler version requirements
-cc_version = methods.get_compiler_version(env) or {
- "major": None,
- "minor": None,
- "patch": None,
- "metadata1": None,
- "metadata2": None,
- "date": None,
-}
-cc_version_major = int(cc_version["major"] or -1)
-cc_version_minor = int(cc_version["minor"] or -1)
-cc_version_metadata1 = cc_version["metadata1"] or ""
-
-if methods.using_gcc(env):
- if cc_version_major == -1:
- print_warning(
- "Couldn't detect compiler version, skipping version checks. "
- "Build may fail if the compiler doesn't support C++17 fully."
- )
- elif cc_version_major < 9:
+cc_version = methods.get_compiler_version(env)
+cc_version_major = cc_version["major"]
+cc_version_minor = cc_version["minor"]
+cc_version_metadata1 = cc_version["metadata1"]
+
+if cc_version_major == -1:
+ print_warning(
+ "Couldn't detect compiler version, skipping version checks. "
+ "Build may fail if the compiler doesn't support C++17 fully."
+ )
+elif methods.using_gcc(env):
+ if cc_version_major < 9:
print_error(
"Detected GCC version older than 9, which does not fully support "
"C++17, or has bugs when compiling Godot. Supported versions are 9 "
@@ -678,17 +670,12 @@ if methods.using_gcc(env):
print_warning("GCC < 8 doesn't support -ffile-prefix-map, disabling `debug_paths_relative` option.")
env["debug_paths_relative"] = False
elif methods.using_clang(env):
- if cc_version_major == -1:
- print_warning(
- "Couldn't detect compiler version, skipping version checks. "
- "Build may fail if the compiler doesn't support C++17 fully."
- )
# Apple LLVM versions differ from upstream LLVM version \o/, compare
# in https://en.wikipedia.org/wiki/Xcode#Toolchain_versions
- elif env["platform"] == "macos" or env["platform"] == "ios":
+ if env["platform"] == "macos" or env["platform"] == "ios":
vanilla = methods.is_vanilla_clang(env)
if vanilla and cc_version_major < 6:
- print_warning(
+ print_error(
"Detected Clang version older than 6, which does not fully support "
"C++17. Supported versions are Clang 6 and later."
)
@@ -713,6 +700,28 @@ elif methods.using_clang(env):
if env["debug_paths_relative"] and cc_version_major < 10:
print_warning("Clang < 10 doesn't support -ffile-prefix-map, disabling `debug_paths_relative` option.")
env["debug_paths_relative"] = False
+elif env.msvc:
+ # Ensure latest minor builds of Visual Studio 2017/2019.
+ # https://github.com/godotengine/godot/pull/94995#issuecomment-2336464574
+ if cc_version_major == 16 and cc_version_minor < 11:
+ print_error(
+ "Detected Visual Studio 2019 version older than 16.11, which has bugs "
+ "when compiling Godot. Use a newer VS2019 version, or VS2022."
+ )
+ Exit(255)
+ if cc_version_major == 15 and cc_version_minor < 9:
+ print_error(
+ "Detected Visual Studio 2017 version older than 15.9, which has bugs "
+ "when compiling Godot. Use a newer VS2017 version, or VS2019/VS2022."
+ )
+ Exit(255)
+ if cc_version_major < 15:
+ print_error(
+ "Detected Visual Studio 2015 or earlier, which is unsupported in Godot. "
+ "Supported versions are Visual Studio 2017 and later."
+ )
+ Exit(255)
+
# Set optimize and debug_symbols flags.
# "custom" means do nothing and let users set their own optimization flags.
@@ -790,13 +799,17 @@ if env["lto"] != "none":
# This needs to come after `configure`, otherwise we don't have env.msvc.
if not env.msvc:
# Specifying GNU extensions support explicitly, which are supported by
- # both GCC and Clang. Both currently default to gnu11 and gnu++14.
+ # both GCC and Clang. Both currently default to gnu11 and gnu++17.
env.Prepend(CFLAGS=["-std=gnu11"])
env.Prepend(CXXFLAGS=["-std=gnu++17"])
else:
- # MSVC doesn't have clear C standard support, /std only covers C++.
- # We apply it to CCFLAGS (both C and C++ code) in case it impacts C features.
- env.Prepend(CCFLAGS=["/std:c++17"])
+ # MSVC started offering C standard support with Visual Studio 2019 16.8, which covers all
+ # of our supported VS2019 & VS2022 versions; VS2017 will only pass the C++ standard.
+ env.Prepend(CXXFLAGS=["/std:c++17"])
+ if cc_version_major < 16:
+ print_warning("Visual Studio 2017 cannot specify a C-Standard.")
+ else:
+ env.Prepend(CFLAGS=["/std:c11"])
# MSVC is non-conforming with the C++ standard by default, so we enable more conformance.
# Note that this is still not complete conformance, as certain Windows-related headers
# don't compile under complete conformance.
diff --git a/core/core_constants.cpp b/core/core_constants.cpp
index 68af5abf66..25da49fa5c 100644
--- a/core/core_constants.cpp
+++ b/core/core_constants.cpp
@@ -677,6 +677,7 @@ void register_global_constants() {
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_NODE_TYPE);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_HIDE_QUATERNION_EDIT);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_PASSWORD);
+ BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_TOOL_BUTTON);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_MAX);
BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_NONE);
diff --git a/core/extension/extension_api_dump.cpp b/core/extension/extension_api_dump.cpp
index 4042d6b80d..c5f7502c12 100644
--- a/core/extension/extension_api_dump.cpp
+++ b/core/extension/extension_api_dump.cpp
@@ -1017,6 +1017,7 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) {
d2["name"] = String(method_name);
d2["is_const"] = (F.flags & METHOD_FLAG_CONST) ? true : false;
d2["is_static"] = (F.flags & METHOD_FLAG_STATIC) ? true : false;
+ d2["is_required"] = (F.flags & METHOD_FLAG_VIRTUAL_REQUIRED) ? true : false;
d2["is_vararg"] = false;
d2["is_virtual"] = true;
// virtual functions have no hash since no MethodBind is involved
diff --git a/core/extension/make_wrappers.py b/core/extension/make_wrappers.py
index 54f4fd5579..665b6f0f91 100644
--- a/core/extension/make_wrappers.py
+++ b/core/extension/make_wrappers.py
@@ -55,10 +55,10 @@ def generate_mod_version(argcount, const=False, returns=False):
proto_ex = """
#define EXBIND$VER($RETTYPE m_name$ARG) \\
-GDVIRTUAL$VER($RETTYPE_##m_name$ARG)\\
+GDVIRTUAL$VER_REQUIRED($RETTYPE_##m_name$ARG)\\
virtual $RETVAL m_name($FUNCARGS) $CONST override { \\
$RETPRE\\
- GDVIRTUAL_REQUIRED_CALL(_##m_name$CALLARGS$RETREF);\\
+ GDVIRTUAL_CALL(_##m_name$CALLARGS$RETREF);\\
$RETPOST\\
}
"""
diff --git a/core/math/vector4.h b/core/math/vector4.h
index 8632f69f57..9197e3587a 100644
--- a/core/math/vector4.h
+++ b/core/math/vector4.h
@@ -55,16 +55,16 @@ struct [[nodiscard]] Vector4 {
real_t z;
real_t w;
};
- real_t components[4] = { 0, 0, 0, 0 };
+ real_t coord[4] = { 0, 0, 0, 0 };
};
_FORCE_INLINE_ real_t &operator[](int p_axis) {
DEV_ASSERT((unsigned int)p_axis < 4);
- return components[p_axis];
+ return coord[p_axis];
}
_FORCE_INLINE_ const real_t &operator[](int p_axis) const {
DEV_ASSERT((unsigned int)p_axis < 4);
- return components[p_axis];
+ return coord[p_axis];
}
Vector4::Axis min_axis_index() const;
diff --git a/core/object/make_virtuals.py b/core/object/make_virtuals.py
index 7a85753072..3df26e3b73 100644
--- a/core/object/make_virtuals.py
+++ b/core/object/make_virtuals.py
@@ -2,7 +2,6 @@ proto = """#define GDVIRTUAL$VER($RET m_name $ARG)\\
StringName _gdvirtual_##m_name##_sn = #m_name;\\
mutable bool _gdvirtual_##m_name##_initialized = false;\\
mutable void *_gdvirtual_##m_name = nullptr;\\
- template <bool required>\\
_FORCE_INLINE_ bool _gdvirtual_##m_name##_call($CALLARGS) $CONST {\\
ScriptInstance *_script_instance = ((Object *)(this))->get_script_instance();\\
if (_script_instance) {\\
@@ -36,10 +35,8 @@ proto = """#define GDVIRTUAL$VER($RET m_name $ARG)\\
}\\
return true;\\
}\\
- if (required) {\\
- ERR_PRINT_ONCE("Required virtual method " + get_class() + "::" + #m_name + " must be overridden before calling.");\\
- $RVOID\\
- }\\
+ $REQCHECK\\
+ $RVOID\\
return false;\\
}\\
_FORCE_INLINE_ bool _gdvirtual_##m_name##_overridden() const {\\
@@ -73,10 +70,11 @@ proto = """#define GDVIRTUAL$VER($RET m_name $ARG)\\
"""
-def generate_version(argcount, const=False, returns=False):
+def generate_version(argcount, const=False, returns=False, required=False):
s = proto
sproto = str(argcount)
method_info = ""
+ method_flags = "METHOD_FLAG_VIRTUAL"
if returns:
sproto += "R"
s = s.replace("$RET", "m_ret,")
@@ -86,17 +84,27 @@ def generate_version(argcount, const=False, returns=False):
method_info += "\t\tmethod_info.return_val_metadata = GetTypeInfo<m_ret>::METADATA;"
else:
s = s.replace("$RET ", "")
- s = s.replace("\t\t\t$RVOID\\\n", "")
+ s = s.replace("\t\t$RVOID\\\n", "")
s = s.replace("\t\t\t$CALLPTRRETDEF\\\n", "")
if const:
sproto += "C"
+ method_flags += " | METHOD_FLAG_CONST"
s = s.replace("$CONST", "const")
- s = s.replace("$METHOD_FLAGS", "METHOD_FLAG_VIRTUAL | METHOD_FLAG_CONST")
else:
s = s.replace("$CONST ", "")
- s = s.replace("$METHOD_FLAGS", "METHOD_FLAG_VIRTUAL")
+ if required:
+ sproto += "_REQUIRED"
+ method_flags += " | METHOD_FLAG_VIRTUAL_REQUIRED"
+ s = s.replace(
+ "$REQCHECK",
+ 'ERR_PRINT_ONCE("Required virtual method " + get_class() + "::" + #m_name + " must be overridden before calling.");',
+ )
+ else:
+ s = s.replace("\t\t$REQCHECK\\\n", "")
+
+ s = s.replace("$METHOD_FLAGS", method_flags)
s = s.replace("$VER", sproto)
argtext = ""
callargtext = ""
@@ -198,6 +206,10 @@ def run(target, source, env):
txt += generate_version(i, False, True)
txt += generate_version(i, True, False)
txt += generate_version(i, True, True)
+ txt += generate_version(i, False, False, True)
+ txt += generate_version(i, False, True, True)
+ txt += generate_version(i, True, False, True)
+ txt += generate_version(i, True, True, True)
txt += "#endif // GDVIRTUAL_GEN_H\n"
diff --git a/core/object/object.h b/core/object/object.h
index 763e2974b9..110d2790c5 100644
--- a/core/object/object.h
+++ b/core/object/object.h
@@ -87,6 +87,7 @@ enum PropertyHint {
PROPERTY_HINT_PASSWORD,
PROPERTY_HINT_LAYERS_AVOIDANCE,
PROPERTY_HINT_DICTIONARY_TYPE,
+ PROPERTY_HINT_TOOL_BUTTON,
PROPERTY_HINT_MAX,
};
@@ -215,6 +216,7 @@ enum MethodFlags {
METHOD_FLAG_VARARG = 16,
METHOD_FLAG_STATIC = 32,
METHOD_FLAG_OBJECT_CORE = 64,
+ METHOD_FLAG_VIRTUAL_REQUIRED = 128,
METHOD_FLAGS_DEFAULT = METHOD_FLAG_NORMAL,
};
@@ -368,11 +370,8 @@ struct ObjectGDExtension {
#endif
};
-#define GDVIRTUAL_CALL(m_name, ...) _gdvirtual_##m_name##_call<false>(__VA_ARGS__)
-#define GDVIRTUAL_CALL_PTR(m_obj, m_name, ...) m_obj->_gdvirtual_##m_name##_call<false>(__VA_ARGS__)
-
-#define GDVIRTUAL_REQUIRED_CALL(m_name, ...) _gdvirtual_##m_name##_call<true>(__VA_ARGS__)
-#define GDVIRTUAL_REQUIRED_CALL_PTR(m_obj, m_name, ...) m_obj->_gdvirtual_##m_name##_call<true>(__VA_ARGS__)
+#define GDVIRTUAL_CALL(m_name, ...) _gdvirtual_##m_name##_call(__VA_ARGS__)
+#define GDVIRTUAL_CALL_PTR(m_obj, m_name, ...) m_obj->_gdvirtual_##m_name##_call(__VA_ARGS__)
#ifdef DEBUG_METHODS_ENABLED
#define GDVIRTUAL_BIND(m_name, ...) ::ClassDB::add_virtual_method(get_class_static(), _gdvirtual_##m_name##_get_method_info(), true, sarray(__VA_ARGS__));
diff --git a/core/object/script_language_extension.h b/core/object/script_language_extension.h
index bc773c5ad3..d2dce34d4f 100644
--- a/core/object/script_language_extension.h
+++ b/core/object/script_language_extension.h
@@ -57,16 +57,16 @@ public:
EXBIND1RC(bool, inherits_script, const Ref<Script> &)
EXBIND0RC(StringName, get_instance_base_type)
- GDVIRTUAL1RC(GDExtensionPtr<void>, _instance_create, Object *)
+ GDVIRTUAL1RC_REQUIRED(GDExtensionPtr<void>, _instance_create, Object *)
virtual ScriptInstance *instance_create(Object *p_this) override {
GDExtensionPtr<void> ret = nullptr;
- GDVIRTUAL_REQUIRED_CALL(_instance_create, p_this, ret);
+ GDVIRTUAL_CALL(_instance_create, p_this, ret);
return reinterpret_cast<ScriptInstance *>(ret.operator void *());
}
- GDVIRTUAL1RC(GDExtensionPtr<void>, _placeholder_instance_create, Object *)
+ GDVIRTUAL1RC_REQUIRED(GDExtensionPtr<void>, _placeholder_instance_create, Object *)
PlaceHolderScriptInstance *placeholder_instance_create(Object *p_this) override {
GDExtensionPtr<void> ret = nullptr;
- GDVIRTUAL_REQUIRED_CALL(_placeholder_instance_create, p_this, ret);
+ GDVIRTUAL_CALL(_placeholder_instance_create, p_this, ret);
return reinterpret_cast<PlaceHolderScriptInstance *>(ret.operator void *());
}
@@ -76,12 +76,12 @@ public:
EXBIND1(set_source_code, const String &)
EXBIND1R(Error, reload, bool)
- GDVIRTUAL0RC(TypedArray<Dictionary>, _get_documentation)
+ GDVIRTUAL0RC_REQUIRED(TypedArray<Dictionary>, _get_documentation)
GDVIRTUAL0RC(String, _get_class_icon_path)
#ifdef TOOLS_ENABLED
virtual Vector<DocData::ClassDoc> get_documentation() const override {
TypedArray<Dictionary> doc;
- GDVIRTUAL_REQUIRED_CALL(_get_documentation, doc);
+ GDVIRTUAL_CALL(_get_documentation, doc);
Vector<DocData::ClassDoc> class_doc;
for (int i = 0; i < doc.size(); i++) {
@@ -114,10 +114,10 @@ public:
return Script::get_script_method_argument_count(p_method, r_is_valid);
}
- GDVIRTUAL1RC(Dictionary, _get_method_info, const StringName &)
+ GDVIRTUAL1RC_REQUIRED(Dictionary, _get_method_info, const StringName &)
virtual MethodInfo get_method_info(const StringName &p_method) const override {
Dictionary mi;
- GDVIRTUAL_REQUIRED_CALL(_get_method_info, p_method, mi);
+ GDVIRTUAL_CALL(_get_method_info, p_method, mi);
return MethodInfo::from_dict(mi);
}
@@ -133,47 +133,47 @@ public:
EXBIND0RC(ScriptLanguage *, get_language)
EXBIND1RC(bool, has_script_signal, const StringName &)
- GDVIRTUAL0RC(TypedArray<Dictionary>, _get_script_signal_list)
+ GDVIRTUAL0RC_REQUIRED(TypedArray<Dictionary>, _get_script_signal_list)
virtual void get_script_signal_list(List<MethodInfo> *r_signals) const override {
TypedArray<Dictionary> sl;
- GDVIRTUAL_REQUIRED_CALL(_get_script_signal_list, sl);
+ GDVIRTUAL_CALL(_get_script_signal_list, sl);
for (int i = 0; i < sl.size(); i++) {
r_signals->push_back(MethodInfo::from_dict(sl[i]));
}
}
- GDVIRTUAL1RC(bool, _has_property_default_value, const StringName &)
- GDVIRTUAL1RC(Variant, _get_property_default_value, const StringName &)
+ GDVIRTUAL1RC_REQUIRED(bool, _has_property_default_value, const StringName &)
+ GDVIRTUAL1RC_REQUIRED(Variant, _get_property_default_value, const StringName &)
virtual bool get_property_default_value(const StringName &p_property, Variant &r_value) const override {
bool has_dv = false;
- if (!GDVIRTUAL_REQUIRED_CALL(_has_property_default_value, p_property, has_dv) || !has_dv) {
+ if (!GDVIRTUAL_CALL(_has_property_default_value, p_property, has_dv) || !has_dv) {
return false;
}
Variant ret;
- GDVIRTUAL_REQUIRED_CALL(_get_property_default_value, p_property, ret);
+ GDVIRTUAL_CALL(_get_property_default_value, p_property, ret);
r_value = ret;
return true;
}
EXBIND0(update_exports)
- GDVIRTUAL0RC(TypedArray<Dictionary>, _get_script_method_list)
+ GDVIRTUAL0RC_REQUIRED(TypedArray<Dictionary>, _get_script_method_list)
virtual void get_script_method_list(List<MethodInfo> *r_methods) const override {
TypedArray<Dictionary> sl;
- GDVIRTUAL_REQUIRED_CALL(_get_script_method_list, sl);
+ GDVIRTUAL_CALL(_get_script_method_list, sl);
for (int i = 0; i < sl.size(); i++) {
r_methods->push_back(MethodInfo::from_dict(sl[i]));
}
}
- GDVIRTUAL0RC(TypedArray<Dictionary>, _get_script_property_list)
+ GDVIRTUAL0RC_REQUIRED(TypedArray<Dictionary>, _get_script_property_list)
virtual void get_script_property_list(List<PropertyInfo> *r_propertys) const override {
TypedArray<Dictionary> sl;
- GDVIRTUAL_REQUIRED_CALL(_get_script_property_list, sl);
+ GDVIRTUAL_CALL(_get_script_property_list, sl);
for (int i = 0; i < sl.size(); i++) {
r_propertys->push_back(PropertyInfo::from_dict(sl[i]));
}
@@ -181,21 +181,21 @@ public:
EXBIND1RC(int, get_member_line, const StringName &)
- GDVIRTUAL0RC(Dictionary, _get_constants)
+ GDVIRTUAL0RC_REQUIRED(Dictionary, _get_constants)
virtual void get_constants(HashMap<StringName, Variant> *p_constants) override {
Dictionary constants;
- GDVIRTUAL_REQUIRED_CALL(_get_constants, constants);
+ GDVIRTUAL_CALL(_get_constants, constants);
List<Variant> keys;
constants.get_key_list(&keys);
for (const Variant &K : keys) {
p_constants->insert(K, constants[K]);
}
}
- GDVIRTUAL0RC(TypedArray<StringName>, _get_members)
+ GDVIRTUAL0RC_REQUIRED(TypedArray<StringName>, _get_members)
virtual void get_members(HashSet<StringName> *p_members) override {
TypedArray<StringName> members;
- GDVIRTUAL_REQUIRED_CALL(_get_members, members);
+ GDVIRTUAL_CALL(_get_members, members);
for (int i = 0; i < members.size(); i++) {
p_members->insert(members[i]);
}
@@ -203,11 +203,11 @@ public:
EXBIND0RC(bool, is_placeholder_fallback_enabled)
- GDVIRTUAL0RC(Variant, _get_rpc_config)
+ GDVIRTUAL0RC_REQUIRED(Variant, _get_rpc_config)
virtual Variant get_rpc_config() const override {
Variant ret;
- GDVIRTUAL_REQUIRED_CALL(_get_rpc_config, ret);
+ GDVIRTUAL_CALL(_get_rpc_config, ret);
return ret;
}
@@ -233,22 +233,22 @@ public:
/* EDITOR FUNCTIONS */
- GDVIRTUAL0RC(Vector<String>, _get_reserved_words)
+ GDVIRTUAL0RC_REQUIRED(Vector<String>, _get_reserved_words)
virtual void get_reserved_words(List<String> *p_words) const override {
Vector<String> ret;
- GDVIRTUAL_REQUIRED_CALL(_get_reserved_words, ret);
+ GDVIRTUAL_CALL(_get_reserved_words, ret);
for (int i = 0; i < ret.size(); i++) {
p_words->push_back(ret[i]);
}
}
EXBIND1RC(bool, is_control_flow_keyword, const String &)
- GDVIRTUAL0RC(Vector<String>, _get_comment_delimiters)
+ GDVIRTUAL0RC_REQUIRED(Vector<String>, _get_comment_delimiters)
virtual void get_comment_delimiters(List<String> *p_words) const override {
Vector<String> ret;
- GDVIRTUAL_REQUIRED_CALL(_get_comment_delimiters, ret);
+ GDVIRTUAL_CALL(_get_comment_delimiters, ret);
for (int i = 0; i < ret.size(); i++) {
p_words->push_back(ret[i]);
}
@@ -264,11 +264,11 @@ public:
}
}
- GDVIRTUAL0RC(Vector<String>, _get_string_delimiters)
+ GDVIRTUAL0RC_REQUIRED(Vector<String>, _get_string_delimiters)
virtual void get_string_delimiters(List<String> *p_words) const override {
Vector<String> ret;
- GDVIRTUAL_REQUIRED_CALL(_get_string_delimiters, ret);
+ GDVIRTUAL_CALL(_get_string_delimiters, ret);
for (int i = 0; i < ret.size(); i++) {
p_words->push_back(ret[i]);
}
@@ -276,11 +276,11 @@ public:
EXBIND3RC(Ref<Script>, make_template, const String &, const String &, const String &)
- GDVIRTUAL1RC(TypedArray<Dictionary>, _get_built_in_templates, StringName)
+ GDVIRTUAL1RC_REQUIRED(TypedArray<Dictionary>, _get_built_in_templates, StringName)
virtual Vector<ScriptTemplate> get_built_in_templates(const StringName &p_object) override {
TypedArray<Dictionary> ret;
- GDVIRTUAL_REQUIRED_CALL(_get_built_in_templates, p_object, ret);
+ GDVIRTUAL_CALL(_get_built_in_templates, p_object, ret);
Vector<ScriptTemplate> stret;
for (int i = 0; i < ret.size(); i++) {
Dictionary d = ret[i];
@@ -304,10 +304,10 @@ public:
EXBIND0R(bool, is_using_templates)
- GDVIRTUAL6RC(Dictionary, _validate, const String &, const String &, bool, bool, bool, bool)
+ GDVIRTUAL6RC_REQUIRED(Dictionary, _validate, const String &, const String &, bool, bool, bool, bool)
virtual bool validate(const String &p_script, const String &p_path = "", List<String> *r_functions = nullptr, List<ScriptError> *r_errors = nullptr, List<Warning> *r_warnings = nullptr, HashSet<int> *r_safe_lines = nullptr) const override {
Dictionary ret;
- GDVIRTUAL_REQUIRED_CALL(_validate, p_script, p_path, r_functions != nullptr, r_errors != nullptr, r_warnings != nullptr, r_safe_lines != nullptr, ret);
+ GDVIRTUAL_CALL(_validate, p_script, p_path, r_functions != nullptr, r_errors != nullptr, r_warnings != nullptr, r_safe_lines != nullptr, ret);
if (!ret.has("valid")) {
return false;
}
@@ -371,10 +371,10 @@ public:
}
EXBIND1RC(String, validate_path, const String &)
- GDVIRTUAL0RC(Object *, _create_script)
+ GDVIRTUAL0RC_REQUIRED(Object *, _create_script)
Script *create_script() const override {
Object *ret = nullptr;
- GDVIRTUAL_REQUIRED_CALL(_create_script, ret);
+ GDVIRTUAL_CALL(_create_script, ret);
return Object::cast_to<Script>(ret);
}
#ifndef DISABLE_DEPRECATED
@@ -400,11 +400,11 @@ public:
return ScriptNameCasing::SCRIPT_NAME_CASING_SNAKE_CASE;
}
- GDVIRTUAL3RC(Dictionary, _complete_code, const String &, const String &, Object *)
+ GDVIRTUAL3RC_REQUIRED(Dictionary, _complete_code, const String &, const String &, Object *)
virtual Error complete_code(const String &p_code, const String &p_path, Object *p_owner, List<CodeCompletionOption> *r_options, bool &r_force, String &r_call_hint) override {
Dictionary ret;
- GDVIRTUAL_REQUIRED_CALL(_complete_code, p_code, p_path, p_owner, ret);
+ GDVIRTUAL_CALL(_complete_code, p_code, p_path, p_owner, ret);
if (!ret.has("result")) {
return ERR_UNAVAILABLE;
}
@@ -449,11 +449,11 @@ public:
return result;
}
- GDVIRTUAL4RC(Dictionary, _lookup_code, const String &, const String &, const String &, Object *)
+ GDVIRTUAL4RC_REQUIRED(Dictionary, _lookup_code, const String &, const String &, const String &, Object *)
virtual Error lookup_code(const String &p_code, const String &p_symbol, const String &p_path, Object *p_owner, LookupResult &r_result) override {
Dictionary ret;
- GDVIRTUAL_REQUIRED_CALL(_lookup_code, p_code, p_symbol, p_path, p_owner, ret);
+ GDVIRTUAL_CALL(_lookup_code, p_code, p_symbol, p_path, p_owner, ret);
if (!ret.has("result")) {
return ERR_UNAVAILABLE;
}
@@ -474,10 +474,10 @@ public:
return result;
}
- GDVIRTUAL3RC(String, _auto_indent_code, const String &, int, int)
+ GDVIRTUAL3RC_REQUIRED(String, _auto_indent_code, const String &, int, int)
virtual void auto_indent_code(String &p_code, int p_from_line, int p_to_line) const override {
String ret;
- GDVIRTUAL_REQUIRED_CALL(_auto_indent_code, p_code, p_from_line, p_to_line, ret);
+ GDVIRTUAL_CALL(_auto_indent_code, p_code, p_from_line, p_to_line, ret);
p_code = ret;
}
EXBIND2(add_global_constant, const StringName &, const Variant &)
@@ -496,10 +496,10 @@ public:
EXBIND1RC(String, debug_get_stack_level_function, int)
EXBIND1RC(String, debug_get_stack_level_source, int)
- GDVIRTUAL3R(Dictionary, _debug_get_stack_level_locals, int, int, int)
+ GDVIRTUAL3R_REQUIRED(Dictionary, _debug_get_stack_level_locals, int, int, int)
virtual void debug_get_stack_level_locals(int p_level, List<String> *p_locals, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1) override {
Dictionary ret;
- GDVIRTUAL_REQUIRED_CALL(_debug_get_stack_level_locals, p_level, p_max_subitems, p_max_depth, ret);
+ GDVIRTUAL_CALL(_debug_get_stack_level_locals, p_level, p_max_subitems, p_max_depth, ret);
if (ret.size() == 0) {
return;
}
@@ -516,10 +516,10 @@ public:
}
}
}
- GDVIRTUAL3R(Dictionary, _debug_get_stack_level_members, int, int, int)
+ GDVIRTUAL3R_REQUIRED(Dictionary, _debug_get_stack_level_members, int, int, int)
virtual void debug_get_stack_level_members(int p_level, List<String> *p_members, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1) override {
Dictionary ret;
- GDVIRTUAL_REQUIRED_CALL(_debug_get_stack_level_members, p_level, p_max_subitems, p_max_depth, ret);
+ GDVIRTUAL_CALL(_debug_get_stack_level_members, p_level, p_max_subitems, p_max_depth, ret);
if (ret.size() == 0) {
return;
}
@@ -536,17 +536,17 @@ public:
}
}
}
- GDVIRTUAL1R(GDExtensionPtr<void>, _debug_get_stack_level_instance, int)
+ GDVIRTUAL1R_REQUIRED(GDExtensionPtr<void>, _debug_get_stack_level_instance, int)
virtual ScriptInstance *debug_get_stack_level_instance(int p_level) override {
GDExtensionPtr<void> ret = nullptr;
- GDVIRTUAL_REQUIRED_CALL(_debug_get_stack_level_instance, p_level, ret);
+ GDVIRTUAL_CALL(_debug_get_stack_level_instance, p_level, ret);
return reinterpret_cast<ScriptInstance *>(ret.operator void *());
}
- GDVIRTUAL2R(Dictionary, _debug_get_globals, int, int)
+ GDVIRTUAL2R_REQUIRED(Dictionary, _debug_get_globals, int, int)
virtual void debug_get_globals(List<String> *p_globals, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1) override {
Dictionary ret;
- GDVIRTUAL_REQUIRED_CALL(_debug_get_globals, p_max_subitems, p_max_depth, ret);
+ GDVIRTUAL_CALL(_debug_get_globals, p_max_subitems, p_max_depth, ret);
if (ret.size() == 0) {
return;
}
@@ -566,10 +566,10 @@ public:
EXBIND4R(String, debug_parse_stack_level_expression, int, const String &, int, int)
- GDVIRTUAL0R(TypedArray<Dictionary>, _debug_get_current_stack_info)
+ GDVIRTUAL0R_REQUIRED(TypedArray<Dictionary>, _debug_get_current_stack_info)
virtual Vector<StackInfo> debug_get_current_stack_info() override {
TypedArray<Dictionary> ret;
- GDVIRTUAL_REQUIRED_CALL(_debug_get_current_stack_info, ret);
+ GDVIRTUAL_CALL(_debug_get_current_stack_info, ret);
Vector<StackInfo> sret;
for (const Variant &var : ret) {
StackInfo si;
@@ -590,29 +590,29 @@ public:
EXBIND2(reload_tool_script, const Ref<Script> &, bool)
/* LOADER FUNCTIONS */
- GDVIRTUAL0RC(PackedStringArray, _get_recognized_extensions)
+ GDVIRTUAL0RC_REQUIRED(PackedStringArray, _get_recognized_extensions)
virtual void get_recognized_extensions(List<String> *p_extensions) const override {
PackedStringArray ret;
- GDVIRTUAL_REQUIRED_CALL(_get_recognized_extensions, ret);
+ GDVIRTUAL_CALL(_get_recognized_extensions, ret);
for (int i = 0; i < ret.size(); i++) {
p_extensions->push_back(ret[i]);
}
}
- GDVIRTUAL0RC(TypedArray<Dictionary>, _get_public_functions)
+ GDVIRTUAL0RC_REQUIRED(TypedArray<Dictionary>, _get_public_functions)
virtual void get_public_functions(List<MethodInfo> *p_functions) const override {
TypedArray<Dictionary> ret;
- GDVIRTUAL_REQUIRED_CALL(_get_public_functions, ret);
+ GDVIRTUAL_CALL(_get_public_functions, ret);
for (const Variant &var : ret) {
MethodInfo mi = MethodInfo::from_dict(var);
p_functions->push_back(mi);
}
}
- GDVIRTUAL0RC(Dictionary, _get_public_constants)
+ GDVIRTUAL0RC_REQUIRED(Dictionary, _get_public_constants)
virtual void get_public_constants(List<Pair<String, Variant>> *p_constants) const override {
Dictionary ret;
- GDVIRTUAL_REQUIRED_CALL(_get_public_constants, ret);
+ GDVIRTUAL_CALL(_get_public_constants, ret);
for (int i = 0; i < ret.size(); i++) {
Dictionary d = ret[i];
ERR_CONTINUE(!d.has("name"));
@@ -620,10 +620,10 @@ public:
p_constants->push_back(Pair<String, Variant>(d["name"], d["value"]));
}
}
- GDVIRTUAL0RC(TypedArray<Dictionary>, _get_public_annotations)
+ GDVIRTUAL0RC_REQUIRED(TypedArray<Dictionary>, _get_public_annotations)
virtual void get_public_annotations(List<MethodInfo> *p_annotations) const override {
TypedArray<Dictionary> ret;
- GDVIRTUAL_REQUIRED_CALL(_get_public_annotations, ret);
+ GDVIRTUAL_CALL(_get_public_annotations, ret);
for (const Variant &var : ret) {
MethodInfo mi = MethodInfo::from_dict(var);
p_annotations->push_back(mi);
@@ -634,19 +634,19 @@ public:
EXBIND0(profiling_stop)
EXBIND1(profiling_set_save_native_calls, bool)
- GDVIRTUAL2R(int, _profiling_get_accumulated_data, GDExtensionPtr<ScriptLanguageExtensionProfilingInfo>, int)
+ GDVIRTUAL2R_REQUIRED(int, _profiling_get_accumulated_data, GDExtensionPtr<ScriptLanguageExtensionProfilingInfo>, int)
virtual int profiling_get_accumulated_data(ProfilingInfo *p_info_arr, int p_info_max) override {
int ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_profiling_get_accumulated_data, p_info_arr, p_info_max, ret);
+ GDVIRTUAL_CALL(_profiling_get_accumulated_data, p_info_arr, p_info_max, ret);
return ret;
}
- GDVIRTUAL2R(int, _profiling_get_frame_data, GDExtensionPtr<ScriptLanguageExtensionProfilingInfo>, int)
+ GDVIRTUAL2R_REQUIRED(int, _profiling_get_frame_data, GDExtensionPtr<ScriptLanguageExtensionProfilingInfo>, int)
virtual int profiling_get_frame_data(ProfilingInfo *p_info_arr, int p_info_max) override {
int ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_profiling_get_frame_data, p_info_arr, p_info_max, ret);
+ GDVIRTUAL_CALL(_profiling_get_frame_data, p_info_arr, p_info_max, ret);
return ret;
}
@@ -654,11 +654,11 @@ public:
EXBIND1RC(bool, handles_global_class_type, const String &)
- GDVIRTUAL1RC(Dictionary, _get_global_class_name, const String &)
+ GDVIRTUAL1RC_REQUIRED(Dictionary, _get_global_class_name, const String &)
virtual String get_global_class_name(const String &p_path, String *r_base_type = nullptr, String *r_icon_path = nullptr) const override {
Dictionary ret;
- GDVIRTUAL_REQUIRED_CALL(_get_global_class_name, p_path, ret);
+ GDVIRTUAL_CALL(_get_global_class_name, p_path, ret);
if (!ret.has("name")) {
return String();
}
diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp
index 391a203d5b..e6f7492a18 100644
--- a/core/string/ustring.cpp
+++ b/core/string/ustring.cpp
@@ -221,18 +221,35 @@ void CharString::copy_from(const char *p_cstr) {
/* String */
/*************************************************************************/
-Error String::parse_url(String &r_scheme, String &r_host, int &r_port, String &r_path) const {
- // Splits the URL into scheme, host, port, path. Strip credentials when present.
+Error String::parse_url(String &r_scheme, String &r_host, int &r_port, String &r_path, String &r_fragment) const {
+ // Splits the URL into scheme, host, port, path, fragment. Strip credentials when present.
String base = *this;
r_scheme = "";
r_host = "";
r_port = 0;
r_path = "";
+ r_fragment = "";
+
int pos = base.find("://");
// Scheme
if (pos != -1) {
- r_scheme = base.substr(0, pos + 3).to_lower();
- base = base.substr(pos + 3, base.length() - pos - 3);
+ bool is_scheme_valid = true;
+ for (int i = 0; i < pos; i++) {
+ if (!is_ascii_alphanumeric_char(base[i]) && base[i] != '+' && base[i] != '-' && base[i] != '.') {
+ is_scheme_valid = false;
+ break;
+ }
+ }
+ if (is_scheme_valid) {
+ r_scheme = base.substr(0, pos + 3).to_lower();
+ base = base.substr(pos + 3, base.length() - pos - 3);
+ }
+ }
+ pos = base.find("#");
+ // Fragment
+ if (pos != -1) {
+ r_fragment = base.substr(pos + 1);
+ base = base.substr(0, pos);
}
pos = base.find("/");
// Path
diff --git a/core/string/ustring.h b/core/string/ustring.h
index 5d4b209c25..aa62c9cb18 100644
--- a/core/string/ustring.h
+++ b/core/string/ustring.h
@@ -452,7 +452,7 @@ public:
String c_escape_multiline() const;
String c_unescape() const;
String json_escape() const;
- Error parse_url(String &r_scheme, String &r_host, int &r_port, String &r_path) const;
+ Error parse_url(String &r_scheme, String &r_host, int &r_port, String &r_path, String &r_fragment) const;
String property_name_encode() const;
diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp
index 63fb5e8d94..ab33c9db55 100644
--- a/core/variant/variant_call.cpp
+++ b/core/variant/variant_call.cpp
@@ -657,7 +657,23 @@ static _FORCE_INLINE_ void vc_ptrcall(void (*method)(T *, P...), void *p_base, c
} \
};
+#define VARCALL_PACKED_GETTER(m_packed_type, m_return_type) \
+ static m_return_type func_##m_packed_type##_get(m_packed_type *p_instance, int64_t p_index) { \
+ return p_instance->get(p_index); \
+ }
+
struct _VariantCall {
+ VARCALL_PACKED_GETTER(PackedByteArray, uint8_t)
+ VARCALL_PACKED_GETTER(PackedColorArray, Color)
+ VARCALL_PACKED_GETTER(PackedFloat32Array, float)
+ VARCALL_PACKED_GETTER(PackedFloat64Array, double)
+ VARCALL_PACKED_GETTER(PackedInt32Array, int32_t)
+ VARCALL_PACKED_GETTER(PackedInt64Array, int64_t)
+ VARCALL_PACKED_GETTER(PackedStringArray, String)
+ VARCALL_PACKED_GETTER(PackedVector2Array, Vector2)
+ VARCALL_PACKED_GETTER(PackedVector3Array, Vector3)
+ VARCALL_PACKED_GETTER(PackedVector4Array, Vector4)
+
static String func_PackedByteArray_get_string_from_ascii(PackedByteArray *p_instance) {
String s;
if (p_instance->size() > 0) {
@@ -2268,6 +2284,7 @@ static void _register_variant_builtin_methods_misc() {
bind_method(Dictionary, duplicate, sarray("deep"), varray(false));
bind_method(Dictionary, get, sarray("key", "default"), varray(Variant()));
bind_method(Dictionary, get_or_add, sarray("key", "default"), varray(Variant()));
+ bind_method(Dictionary, set, sarray("key", "value"), varray());
bind_method(Dictionary, is_typed, sarray(), varray());
bind_method(Dictionary, is_typed_key, sarray(), varray());
bind_method(Dictionary, is_typed_value, sarray(), varray());
@@ -2293,6 +2310,8 @@ static void _register_variant_builtin_methods_array() {
bind_method(Array, clear, sarray(), varray());
bind_method(Array, hash, sarray(), varray());
bind_method(Array, assign, sarray("array"), varray());
+ bind_method(Array, get, sarray("index"), varray());
+ bind_method(Array, set, sarray("index", "value"), varray());
bind_method(Array, push_back, sarray("value"), varray());
bind_method(Array, push_front, sarray("value"), varray());
bind_method(Array, append, sarray("value"), varray());
@@ -2337,6 +2356,18 @@ static void _register_variant_builtin_methods_array() {
bind_method(Array, make_read_only, sarray(), varray());
bind_method(Array, is_read_only, sarray(), varray());
+ /* Packed*Array get (see VARCALL_PACKED_GETTER macro) */
+ bind_function(PackedByteArray, get, _VariantCall::func_PackedByteArray_get, sarray("index"), varray());
+ bind_function(PackedColorArray, get, _VariantCall::func_PackedColorArray_get, sarray("index"), varray());
+ bind_function(PackedFloat32Array, get, _VariantCall::func_PackedFloat32Array_get, sarray("index"), varray());
+ bind_function(PackedFloat64Array, get, _VariantCall::func_PackedFloat64Array_get, sarray("index"), varray());
+ bind_function(PackedInt32Array, get, _VariantCall::func_PackedInt32Array_get, sarray("index"), varray());
+ bind_function(PackedInt64Array, get, _VariantCall::func_PackedInt64Array_get, sarray("index"), varray());
+ bind_function(PackedStringArray, get, _VariantCall::func_PackedStringArray_get, sarray("index"), varray());
+ bind_function(PackedVector2Array, get, _VariantCall::func_PackedVector2Array_get, sarray("index"), varray());
+ bind_function(PackedVector3Array, get, _VariantCall::func_PackedVector3Array_get, sarray("index"), varray());
+ bind_function(PackedVector4Array, get, _VariantCall::func_PackedVector4Array_get, sarray("index"), varray());
+
/* Byte Array */
bind_method(PackedByteArray, size, sarray(), varray());
bind_method(PackedByteArray, is_empty, sarray(), varray());
diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml
index a86f41cd9c..63d20242d6 100644
--- a/doc/classes/@GlobalScope.xml
+++ b/doc/classes/@GlobalScope.xml
@@ -2933,7 +2933,15 @@
<constant name="PROPERTY_HINT_PASSWORD" value="36" enum="PropertyHint">
Hints that a string property is a password, and every character is replaced with the secret character.
</constant>
- <constant name="PROPERTY_HINT_MAX" value="39" enum="PropertyHint">
+ <constant name="PROPERTY_HINT_TOOL_BUTTON" value="39" enum="PropertyHint">
+ Hints that a [Callable] property should be displayed as a clickable button. When the button is pressed, the callable is called. The hint string specifies the button text and optionally an icon from the [code]"EditorIcons"[/code] theme type.
+ [codeblock lang=text]
+ "Click me!" - A button with the text "Click me!" and the default "Callable" icon.
+ "Click me!,ColorRect" - A button with the text "Click me!" and the "ColorRect" icon.
+ [/codeblock]
+ [b]Note:[/b] A [Callable] cannot be properly serialized and stored in a file, so it is recommended to use [constant PROPERTY_USAGE_EDITOR] instead of [constant PROPERTY_USAGE_DEFAULT].
+ </constant>
+ <constant name="PROPERTY_HINT_MAX" value="40" enum="PropertyHint">
Represents the size of the [enum PropertyHint] enum.
</constant>
<constant name="PROPERTY_USAGE_NONE" value="0" enum="PropertyUsageFlags" is_bitfield="true">
diff --git a/doc/classes/Animation.xml b/doc/classes/Animation.xml
index 942299cc7e..609d7eff39 100644
--- a/doc/classes/Animation.xml
+++ b/doc/classes/Animation.xml
@@ -34,6 +34,14 @@
<link title="Animation documentation index">$DOCS_URL/tutorials/animation/index.html</link>
</tutorials>
<methods>
+ <method name="add_marker">
+ <return type="void" />
+ <param index="0" name="name" type="StringName" />
+ <param index="1" name="time" type="float" />
+ <description>
+ Adds a marker to this Animation.
+ </description>
+ </method>
<method name="add_track">
<return type="int" />
<param index="0" name="type" type="int" enum="Animation.TrackType" />
@@ -271,12 +279,60 @@
Returns the index of the specified track. If the track is not found, return -1.
</description>
</method>
+ <method name="get_marker_at_time" qualifiers="const">
+ <return type="StringName" />
+ <param index="0" name="time" type="float" />
+ <description>
+ Returns the name of the marker located at the given time.
+ </description>
+ </method>
+ <method name="get_marker_color" qualifiers="const">
+ <return type="Color" />
+ <param index="0" name="name" type="StringName" />
+ <description>
+ Returns the given marker's color.
+ </description>
+ </method>
+ <method name="get_marker_names" qualifiers="const">
+ <return type="PackedStringArray" />
+ <description>
+ Returns every marker in this Animation, sorted ascending by time.
+ </description>
+ </method>
+ <method name="get_marker_time" qualifiers="const">
+ <return type="float" />
+ <param index="0" name="name" type="StringName" />
+ <description>
+ Returns the given marker's time.
+ </description>
+ </method>
+ <method name="get_next_marker" qualifiers="const">
+ <return type="StringName" />
+ <param index="0" name="time" type="float" />
+ <description>
+ Returns the closest marker that comes after the given time. If no such marker exists, an empty string is returned.
+ </description>
+ </method>
+ <method name="get_prev_marker" qualifiers="const">
+ <return type="StringName" />
+ <param index="0" name="time" type="float" />
+ <description>
+ Returns the closest marker that comes before the given time. If no such marker exists, an empty string is returned.
+ </description>
+ </method>
<method name="get_track_count" qualifiers="const">
<return type="int" />
<description>
Returns the amount of tracks in the animation.
</description>
</method>
+ <method name="has_marker" qualifiers="const">
+ <return type="bool" />
+ <param index="0" name="name" type="StringName" />
+ <description>
+ Returns [code]true[/code] if this Animation contains a marker with the given name.
+ </description>
+ </method>
<method name="method_track_get_name" qualifiers="const">
<return type="StringName" />
<param index="0" name="track_idx" type="int" />
@@ -293,6 +349,15 @@
Returns the arguments values to be called on a method track for a given key in a given track.
</description>
</method>
+ <method name="optimize">
+ <return type="void" />
+ <param index="0" name="allowed_velocity_err" type="float" default="0.01" />
+ <param index="1" name="allowed_angular_err" type="float" default="0.01" />
+ <param index="2" name="precision" type="int" default="3" />
+ <description>
+ Optimize the animation and all its tracks in-place. This will preserve only as many keys as are necessary to keep the animation within the specified bounds.
+ </description>
+ </method>
<method name="position_track_insert_key">
<return type="int" />
<param index="0" name="track_idx" type="int" />
@@ -311,6 +376,13 @@
Returns the interpolated position value at the given time (in seconds). The [param track_idx] must be the index of a 3D position track.
</description>
</method>
+ <method name="remove_marker">
+ <return type="void" />
+ <param index="0" name="name" type="StringName" />
+ <description>
+ Removes the marker with the given name from this Animation.
+ </description>
+ </method>
<method name="remove_track">
<return type="void" />
<param index="0" name="track_idx" type="int" />
@@ -354,6 +426,14 @@
Returns the interpolated scale value at the given time (in seconds). The [param track_idx] must be the index of a 3D scale track.
</description>
</method>
+ <method name="set_marker_color">
+ <return type="void" />
+ <param index="0" name="name" type="StringName" />
+ <param index="1" name="color" type="Color" />
+ <description>
+ Sets the given marker's color.
+ </description>
+ </method>
<method name="track_find_key" qualifiers="const">
<return type="int" />
<param index="0" name="track_idx" type="int" />
diff --git a/doc/classes/AnimationPlayer.xml b/doc/classes/AnimationPlayer.xml
index 1ca8ac2fa5..9aeb4b7162 100644
--- a/doc/classes/AnimationPlayer.xml
+++ b/doc/classes/AnimationPlayer.xml
@@ -75,6 +75,24 @@
Returns the node which node path references will travel from.
</description>
</method>
+ <method name="get_section_end_time" qualifiers="const">
+ <return type="float" />
+ <description>
+ Returns the end time of the section currently being played.
+ </description>
+ </method>
+ <method name="get_section_start_time" qualifiers="const">
+ <return type="float" />
+ <description>
+ Returns the start time of the section currently being played.
+ </description>
+ </method>
+ <method name="has_section" qualifiers="const">
+ <return type="bool" />
+ <description>
+ Returns [code]true[/code] if an animation is currently playing with section.
+ </description>
+ </method>
<method name="is_playing" qualifiers="const">
<return type="bool" />
<description>
@@ -110,6 +128,54 @@
This method is a shorthand for [method play] with [code]custom_speed = -1.0[/code] and [code]from_end = true[/code], so see its description for more information.
</description>
</method>
+ <method name="play_section">
+ <return type="void" />
+ <param index="0" name="name" type="StringName" default="&amp;&quot;&quot;" />
+ <param index="1" name="start_time" type="float" default="-1" />
+ <param index="2" name="end_time" type="float" default="-1" />
+ <param index="3" name="custom_blend" type="float" default="-1" />
+ <param index="4" name="custom_speed" type="float" default="1.0" />
+ <param index="5" name="from_end" type="bool" default="false" />
+ <description>
+ Plays the animation with key [param name] and the section starting from [param start_time] and ending on [param end_time]. See also [method play].
+ Setting [param start_time] to a value outside the range of the animation means the start of the animation will be used instead, and setting [param end_time] to a value outside the range of the animation means the end of the animation will be used instead. [param start_time] cannot be equal to [param end_time].
+ </description>
+ </method>
+ <method name="play_section_backwards">
+ <return type="void" />
+ <param index="0" name="name" type="StringName" default="&amp;&quot;&quot;" />
+ <param index="1" name="start_time" type="float" default="-1" />
+ <param index="2" name="end_time" type="float" default="-1" />
+ <param index="3" name="custom_blend" type="float" default="-1" />
+ <description>
+ Plays the animation with key [param name] and the section starting from [param start_time] and ending on [param end_time] in reverse.
+ This method is a shorthand for [method play_section] with [code]custom_speed = -1.0[/code] and [code]from_end = true[/code], see its description for more information.
+ </description>
+ </method>
+ <method name="play_section_with_markers">
+ <return type="void" />
+ <param index="0" name="name" type="StringName" default="&amp;&quot;&quot;" />
+ <param index="1" name="start_marker" type="StringName" default="&amp;&quot;&quot;" />
+ <param index="2" name="end_marker" type="StringName" default="&amp;&quot;&quot;" />
+ <param index="3" name="custom_blend" type="float" default="-1" />
+ <param index="4" name="custom_speed" type="float" default="1.0" />
+ <param index="5" name="from_end" type="bool" default="false" />
+ <description>
+ Plays the animation with key [param name] and the section starting from [param start_marker] and ending on [param end_marker].
+ If the start marker is empty, the section starts from the beginning of the animation. If the end marker is empty, the section ends on the end of the animation. See also [method play].
+ </description>
+ </method>
+ <method name="play_section_with_markers_backwards">
+ <return type="void" />
+ <param index="0" name="name" type="StringName" default="&amp;&quot;&quot;" />
+ <param index="1" name="start_marker" type="StringName" default="&amp;&quot;&quot;" />
+ <param index="2" name="end_marker" type="StringName" default="&amp;&quot;&quot;" />
+ <param index="3" name="custom_blend" type="float" default="-1" />
+ <description>
+ Plays the animation with key [param name] and the section starting from [param start_marker] and ending on [param end_marker] in reverse.
+ This method is a shorthand for [method play_section_with_markers] with [code]custom_speed = -1.0[/code] and [code]from_end = true[/code], see its description for more information.
+ </description>
+ </method>
<method name="play_with_capture">
<return type="void" />
<param index="0" name="name" type="StringName" default="&amp;&quot;&quot;" />
@@ -139,6 +205,12 @@
[b]Note:[/b] If a looped animation is currently playing, the queued animation will never play unless the looped animation is stopped somehow.
</description>
</method>
+ <method name="reset_section">
+ <return type="void" />
+ <description>
+ Resets the current section if section is set.
+ </description>
+ </method>
<method name="seek">
<return type="void" />
<param index="0" name="seconds" type="float" />
@@ -180,6 +252,23 @@
Sets the node which node path references will travel from.
</description>
</method>
+ <method name="set_section">
+ <return type="void" />
+ <param index="0" name="start_time" type="float" default="-1" />
+ <param index="1" name="end_time" type="float" default="-1" />
+ <description>
+ Changes the start and end times of the section being played. The current playback position will be clamped within the new section. See also [method play_section].
+ </description>
+ </method>
+ <method name="set_section_with_markers">
+ <return type="void" />
+ <param index="0" name="start_marker" type="StringName" default="&amp;&quot;&quot;" />
+ <param index="1" name="end_marker" type="StringName" default="&amp;&quot;&quot;" />
+ <description>
+ Changes the start and end markers of the section being played. The current playback position will be clamped within the new section. See also [method play_section_with_markers].
+ If the argument is empty, the section uses the beginning or end of the animation. If both are empty, it means that the section is not set.
+ </description>
+ </method>
<method name="stop">
<return type="void" />
<param index="0" name="keep_state" type="bool" default="false" />
diff --git a/doc/classes/Array.xml b/doc/classes/Array.xml
index adb6be1070..1b7dbf7c41 100644
--- a/doc/classes/Array.xml
+++ b/doc/classes/Array.xml
@@ -422,6 +422,13 @@
[b]Note:[/b] Unlike with the [code][][/code] operator ([code]array[0][/code]), an error is generated without stopping project execution.
</description>
</method>
+ <method name="get" qualifiers="const">
+ <return type="Variant" />
+ <param index="0" name="index" type="int" />
+ <description>
+ Returns the element at the given [param index] in the array. This is the same as using the [code][][/code] operator ([code]array[index][/code]).
+ </description>
+ </method>
<method name="get_typed_builtin" qualifiers="const">
<return type="int" />
<description>
@@ -693,6 +700,14 @@
Returns the index of the [b]last[/b] element of the array that causes [param method] to return [code]true[/code], or [code]-1[/code] if there are none. The search's start can be specified with [param from], continuing to the beginning of the array. This method is the reverse of [method find_custom].
</description>
</method>
+ <method name="set">
+ <return type="void" />
+ <param index="0" name="index" type="int" />
+ <param index="1" name="value" type="Variant" />
+ <description>
+ Sets the value of the element at the given [param index] to the given [param value]. This will not change the size of the array, it only changes the value at an index already in the array. This is the same as using the [code][][/code] operator ([code]array[index] = value[/code]).
+ </description>
+ </method>
<method name="shuffle">
<return type="void" />
<description>
diff --git a/doc/classes/BaseButton.xml b/doc/classes/BaseButton.xml
index 18dccfa5a9..13b23d70b7 100644
--- a/doc/classes/BaseButton.xml
+++ b/doc/classes/BaseButton.xml
@@ -75,6 +75,7 @@
</member>
<member name="shortcut_in_tooltip" type="bool" setter="set_shortcut_in_tooltip" getter="is_shortcut_in_tooltip_enabled" default="true">
If [code]true[/code], the button will add information about its shortcut in the tooltip.
+ [b]Note:[/b] This property does nothing when the tooltip control is customized using [method Control._make_custom_tooltip].
</member>
<member name="toggle_mode" type="bool" setter="set_toggle_mode" getter="is_toggle_mode" default="false">
If [code]true[/code], the button is in toggle mode. Makes the button flip state between pressed and unpressed each time its area is clicked.
diff --git a/doc/classes/CameraFeed.xml b/doc/classes/CameraFeed.xml
index 8033c0880b..2b6db13906 100644
--- a/doc/classes/CameraFeed.xml
+++ b/doc/classes/CameraFeed.xml
@@ -45,6 +45,34 @@
[code]copy[/code] will result in FEED_YCBCR
</description>
</method>
+ <method name="set_name">
+ <return type="void" />
+ <param index="0" name="name" type="String" />
+ <description>
+ Sets the camera's name.
+ </description>
+ </method>
+ <method name="set_position">
+ <return type="void" />
+ <param index="0" name="position" type="int" enum="CameraFeed.FeedPosition" />
+ <description>
+ Sets the position of this camera.
+ </description>
+ </method>
+ <method name="set_rgb_image">
+ <return type="void" />
+ <param index="0" name="rgb_image" type="Image" />
+ <description>
+ Sets RGB image for this feed.
+ </description>
+ </method>
+ <method name="set_ycbcr_image">
+ <return type="void" />
+ <param index="0" name="ycbcr_image" type="Image" />
+ <description>
+ Sets YCbCr image for this feed.
+ </description>
+ </method>
</methods>
<members>
<member name="feed_is_active" type="bool" setter="set_active" getter="is_active" default="false">
diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml
index 927ab9ae0e..9d36bc657b 100644
--- a/doc/classes/Control.xml
+++ b/doc/classes/Control.xml
@@ -1057,6 +1057,10 @@
[b]Note:[/b] To look up [Control]'s own items use various [code]get_theme_*[/code] methods without specifying [code]theme_type[/code].
[b]Note:[/b] Theme items are looked for in the tree order, from branch to root, where each [Control] node is checked for its [member theme] property. The earliest match against any type/class name is returned. The project-level Theme and the default Theme are checked last.
</member>
+ <member name="tooltip_auto_translate_mode" type="int" setter="set_tooltip_auto_translate_mode" getter="get_tooltip_auto_translate_mode" enum="Node.AutoTranslateMode" default="0">
+ Defines if tooltip text should automatically change to its translated version depending on the current locale. Uses the same auto translate mode as this control when set to [constant Node.AUTO_TRANSLATE_MODE_INHERIT].
+ [b]Note:[/b] When the tooltip is customized using [method _make_custom_tooltip], this auto translate mode is applied automatically to the returned control.
+ </member>
<member name="tooltip_text" type="String" setter="set_tooltip_text" getter="get_tooltip_text" default="&quot;&quot;">
The default tooltip text. The tooltip appears when the user's mouse cursor stays idle over this control for a few moments, provided that the [member mouse_filter] property is not [constant MOUSE_FILTER_IGNORE]. The time required for the tooltip to appear can be changed with the [member ProjectSettings.gui/timers/tooltip_delay_sec] option. See also [method get_tooltip].
The tooltip popup will use either a default implementation, or a custom one that you can provide by overriding [method _make_custom_tooltip]. The default tooltip includes a [PopupPanel] and [Label] whose theme properties can be customized using [Theme] methods with the [code]"TooltipPanel"[/code] and [code]"TooltipLabel"[/code] respectively. For example:
diff --git a/doc/classes/Dictionary.xml b/doc/classes/Dictionary.xml
index feb2a07e4a..4441c3cb79 100644
--- a/doc/classes/Dictionary.xml
+++ b/doc/classes/Dictionary.xml
@@ -460,6 +460,14 @@
Returns [code]true[/code] if the two dictionaries contain the same keys and values, inner [Dictionary] and [Array] keys and values are compared recursively.
</description>
</method>
+ <method name="set">
+ <return type="bool" />
+ <param index="0" name="key" type="Variant" />
+ <param index="1" name="value" type="Variant" />
+ <description>
+ Sets the value of the element at the given [param key] to the given [param value]. This is the same as using the [code][][/code] operator ([code]array[index] = value[/code]).
+ </description>
+ </method>
<method name="size" qualifiers="const">
<return type="int" />
<description>
diff --git a/doc/classes/GraphEdit.xml b/doc/classes/GraphEdit.xml
index 670df10a89..3451e427f3 100644
--- a/doc/classes/GraphEdit.xml
+++ b/doc/classes/GraphEdit.xml
@@ -399,6 +399,11 @@
Emitted when this [GraphEdit] captures a [code]ui_copy[/code] action ([kbd]Ctrl + C[/kbd] by default). In general, this signal indicates that the selected [GraphElement]s should be copied.
</description>
</signal>
+ <signal name="cut_nodes_request">
+ <description>
+ Emitted when this [GraphEdit] captures a [code]ui_cut[/code] action ([kbd]Ctrl + X[/kbd] by default). In general, this signal indicates that the selected [GraphElement]s should be cut.
+ </description>
+ </signal>
<signal name="delete_nodes_request">
<param index="0" name="nodes" type="StringName[]" />
<description>
diff --git a/doc/classes/HTTPClient.xml b/doc/classes/HTTPClient.xml
index 864c29a2b5..af98636056 100644
--- a/doc/classes/HTTPClient.xml
+++ b/doc/classes/HTTPClient.xml
@@ -12,7 +12,7 @@
[b]Note:[/b] When exporting to Android, make sure to enable the [code]INTERNET[/code] permission in the Android export preset before exporting the project or using one-click deploy. Otherwise, network communication of any kind will be blocked by Android.
[b]Note:[/b] It's recommended to use transport encryption (TLS) and to avoid sending sensitive information (such as login credentials) in HTTP GET URL parameters. Consider using HTTP POST requests or HTTP headers for such information instead.
[b]Note:[/b] When performing HTTP requests from a project exported to Web, keep in mind the remote server may not allow requests from foreign origins due to [url=https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS]CORS[/url]. If you host the server in question, you should modify its backend to allow requests from foreign origins by adding the [code]Access-Control-Allow-Origin: *[/code] HTTP header.
- [b]Note:[/b] TLS support is currently limited to TLS 1.0, TLS 1.1, and TLS 1.2. Attempting to connect to a TLS 1.3-only server will return an error.
+ [b]Note:[/b] TLS support is currently limited to TLSv1.2 and TLSv1.3. Attempting to connect to a server that only supports older (insecure) TLS versions will return an error.
[b]Warning:[/b] TLS certificate revocation and certificate pinning are currently not supported. Revoked certificates are accepted as long as they are otherwise valid. If this is a concern, you may want to use automatically managed certificates with a short validity period.
</description>
<tutorials>
diff --git a/doc/classes/ItemList.xml b/doc/classes/ItemList.xml
index a1e5d9cbd9..c60a2ca887 100644
--- a/doc/classes/ItemList.xml
+++ b/doc/classes/ItemList.xml
@@ -407,13 +407,14 @@
<param index="0" name="at_position" type="Vector2" />
<param index="1" name="mouse_button_index" type="int" />
<description>
- Triggered when any mouse click is issued within the rect of the list but on empty space.
+ Emitted when any mouse click is issued within the rect of the list but on empty space.
+ [param at_position] is the click position in this control's local coordinate system.
</description>
</signal>
<signal name="item_activated">
<param index="0" name="index" type="int" />
<description>
- Triggered when specified list item is activated via double-clicking or by pressing [kbd]Enter[/kbd].
+ Emitted when specified list item is activated via double-clicking or by pressing [kbd]Enter[/kbd].
</description>
</signal>
<signal name="item_clicked">
@@ -421,14 +422,14 @@
<param index="1" name="at_position" type="Vector2" />
<param index="2" name="mouse_button_index" type="int" />
<description>
- Triggered when specified list item has been clicked with any mouse button.
- The click position is also provided to allow appropriate popup of context menus at the correct location.
+ Emitted when specified list item has been clicked with any mouse button.
+ [param at_position] is the click position in this control's local coordinate system.
</description>
</signal>
<signal name="item_selected">
<param index="0" name="index" type="int" />
<description>
- Triggered when specified item has been selected.
+ Emitted when specified item has been selected. Only applicable in single selection mode.
[member allow_reselect] must be enabled to reselect an item.
</description>
</signal>
@@ -436,7 +437,7 @@
<param index="0" name="index" type="int" />
<param index="1" name="selected" type="bool" />
<description>
- Triggered when a multiple selection is altered on a list allowing multiple selection.
+ Emitted when a multiple selection is altered on a list allowing multiple selection.
</description>
</signal>
</signals>
diff --git a/doc/classes/LineEdit.xml b/doc/classes/LineEdit.xml
index 9c460e6d62..41f42392de 100644
--- a/doc/classes/LineEdit.xml
+++ b/doc/classes/LineEdit.xml
@@ -6,11 +6,11 @@
<description>
[LineEdit] provides an input field for editing a single line of text.
- When the [LineEdit] control is focused using the keyboard arrow keys, it will only gain focus and not enter edit mode.
- - To enter edit mode, click on the control with the mouse or press the "ui_text_submit" action (default: [kbd]Enter[/kbd] or [kbd]Kp Enter[/kbd]).
- - To exit edit mode, press "ui_text_submit" or "ui_cancel" (default: [kbd]Escape[/kbd]) actions.
+ - To enter edit mode, click on the control with the mouse or press the [code]ui_text_submit[/code] action (by default [kbd]Enter[/kbd] or [kbd]Kp Enter[/kbd]).
+ - To exit edit mode, press [code]ui_text_submit[/code] or [code]ui_cancel[/code] (by default [kbd]Escape[/kbd]) actions.
- Check [method is_editing] and [signal editing_toggled] for more information.
[b]Important:[/b]
- - Focusing the [LineEdit] with "ui_focus_next" (default: [kbd]Tab[/kbd]) or "ui_focus_prev" (default: [kbd]Shift + Tab[/kbd]) or [method Control.grab_focus] still enters edit mode (for compatibility).
+ - Focusing the [LineEdit] with [code]ui_focus_next[/code] (by default [kbd]Tab[/kbd]) or [code]ui_focus_prev[/code] (by default [kbd]Shift + Tab[/kbd]) or [method Control.grab_focus] still enters edit mode (for compatibility).
[LineEdit] features many built-in shortcuts that are always available ([kbd]Ctrl[/kbd] here maps to [kbd]Cmd[/kbd] on macOS):
- [kbd]Ctrl + C[/kbd]: Copy
- [kbd]Ctrl + X[/kbd]: Cut
@@ -365,7 +365,7 @@
<signal name="text_submitted">
<param index="0" name="new_text" type="String" />
<description>
- Emitted when the user presses [constant KEY_ENTER] on the [LineEdit].
+ Emitted when the user presses the [code]ui_text_submit[/code] action (by default: [kbd]Enter[/kbd] or [kbd]Kp Enter[/kbd]) while the [LineEdit] has focus.
</description>
</signal>
</signals>
diff --git a/doc/classes/PackedByteArray.xml b/doc/classes/PackedByteArray.xml
index e179c111a2..75193ae8ed 100644
--- a/doc/classes/PackedByteArray.xml
+++ b/doc/classes/PackedByteArray.xml
@@ -307,6 +307,13 @@
Searches the array for a value and returns its index or [code]-1[/code] if not found. Optionally, the initial search index can be passed.
</description>
</method>
+ <method name="get" qualifiers="const">
+ <return type="int" />
+ <param index="0" name="index" type="int" />
+ <description>
+ Returns the byte at the given [param index] in the array. This is the same as using the [code][][/code] operator ([code]array[index][/code]).
+ </description>
+ </method>
<method name="get_string_from_ascii" qualifiers="const">
<return type="String" />
<description>
diff --git a/doc/classes/PackedColorArray.xml b/doc/classes/PackedColorArray.xml
index 57295cb1e3..b9cef0ead6 100644
--- a/doc/classes/PackedColorArray.xml
+++ b/doc/classes/PackedColorArray.xml
@@ -94,6 +94,13 @@
Searches the array for a value and returns its index or [code]-1[/code] if not found. Optionally, the initial search index can be passed.
</description>
</method>
+ <method name="get" qualifiers="const">
+ <return type="Color" />
+ <param index="0" name="index" type="int" />
+ <description>
+ Returns the [Color] at the given [param index] in the array. This is the same as using the [code][][/code] operator ([code]array[index][/code]).
+ </description>
+ </method>
<method name="has" qualifiers="const">
<return type="bool" />
<param index="0" name="value" type="Color" />
diff --git a/doc/classes/PackedFloat32Array.xml b/doc/classes/PackedFloat32Array.xml
index 2db1386fd0..d421993b8f 100644
--- a/doc/classes/PackedFloat32Array.xml
+++ b/doc/classes/PackedFloat32Array.xml
@@ -93,6 +93,13 @@
[b]Note:[/b] [constant @GDScript.NAN] doesn't behave the same as other numbers. Therefore, the results from this method may not be accurate if NaNs are included.
</description>
</method>
+ <method name="get" qualifiers="const">
+ <return type="float" />
+ <param index="0" name="index" type="int" />
+ <description>
+ Returns the 32-bit float at the given [param index] in the array. This is the same as using the [code][][/code] operator ([code]array[index][/code]).
+ </description>
+ </method>
<method name="has" qualifiers="const">
<return type="bool" />
<param index="0" name="value" type="float" />
diff --git a/doc/classes/PackedFloat64Array.xml b/doc/classes/PackedFloat64Array.xml
index 0bcee918ed..4622d63258 100644
--- a/doc/classes/PackedFloat64Array.xml
+++ b/doc/classes/PackedFloat64Array.xml
@@ -94,6 +94,13 @@
[b]Note:[/b] [constant @GDScript.NAN] doesn't behave the same as other numbers. Therefore, the results from this method may not be accurate if NaNs are included.
</description>
</method>
+ <method name="get" qualifiers="const">
+ <return type="float" />
+ <param index="0" name="index" type="int" />
+ <description>
+ Returns the 64-bit float at the given [param index] in the array. This is the same as using the [code][][/code] operator ([code]array[index][/code]).
+ </description>
+ </method>
<method name="has" qualifiers="const">
<return type="bool" />
<param index="0" name="value" type="float" />
diff --git a/doc/classes/PackedInt32Array.xml b/doc/classes/PackedInt32Array.xml
index 93b2ae7394..3a3596b2d0 100644
--- a/doc/classes/PackedInt32Array.xml
+++ b/doc/classes/PackedInt32Array.xml
@@ -90,6 +90,13 @@
Searches the array for a value and returns its index or [code]-1[/code] if not found. Optionally, the initial search index can be passed.
</description>
</method>
+ <method name="get" qualifiers="const">
+ <return type="int" />
+ <param index="0" name="index" type="int" />
+ <description>
+ Returns the 32-bit integer at the given [param index] in the array. This is the same as using the [code][][/code] operator ([code]array[index][/code]).
+ </description>
+ </method>
<method name="has" qualifiers="const">
<return type="bool" />
<param index="0" name="value" type="int" />
diff --git a/doc/classes/PackedInt64Array.xml b/doc/classes/PackedInt64Array.xml
index 3d34165915..cfaf012a55 100644
--- a/doc/classes/PackedInt64Array.xml
+++ b/doc/classes/PackedInt64Array.xml
@@ -91,6 +91,13 @@
Searches the array for a value and returns its index or [code]-1[/code] if not found. Optionally, the initial search index can be passed.
</description>
</method>
+ <method name="get" qualifiers="const">
+ <return type="int" />
+ <param index="0" name="index" type="int" />
+ <description>
+ Returns the 64-bit integer at the given [param index] in the array. This is the same as using the [code][][/code] operator ([code]array[index][/code]).
+ </description>
+ </method>
<method name="has" qualifiers="const">
<return type="bool" />
<param index="0" name="value" type="int" />
diff --git a/doc/classes/PackedStringArray.xml b/doc/classes/PackedStringArray.xml
index 621831c7a3..511850d645 100644
--- a/doc/classes/PackedStringArray.xml
+++ b/doc/classes/PackedStringArray.xml
@@ -97,6 +97,13 @@
Searches the array for a value and returns its index or [code]-1[/code] if not found. Optionally, the initial search index can be passed.
</description>
</method>
+ <method name="get" qualifiers="const">
+ <return type="String" />
+ <param index="0" name="index" type="int" />
+ <description>
+ Returns the [String] at the given [param index] in the array. This is the same as using the [code][][/code] operator ([code]array[index][/code]).
+ </description>
+ </method>
<method name="has" qualifiers="const">
<return type="bool" />
<param index="0" name="value" type="String" />
diff --git a/doc/classes/PackedVector2Array.xml b/doc/classes/PackedVector2Array.xml
index 14a3816353..da41812e0b 100644
--- a/doc/classes/PackedVector2Array.xml
+++ b/doc/classes/PackedVector2Array.xml
@@ -98,6 +98,13 @@
[b]Note:[/b] Vectors with [constant @GDScript.NAN] elements don't behave the same as other vectors. Therefore, the results from this method may not be accurate if NaNs are included.
</description>
</method>
+ <method name="get" qualifiers="const">
+ <return type="Vector2" />
+ <param index="0" name="index" type="int" />
+ <description>
+ Returns the [Vector2] at the given [param index] in the array. This is the same as using the [code][][/code] operator ([code]array[index][/code]).
+ </description>
+ </method>
<method name="has" qualifiers="const">
<return type="bool" />
<param index="0" name="value" type="Vector2" />
diff --git a/doc/classes/PackedVector3Array.xml b/doc/classes/PackedVector3Array.xml
index 49220c6fd6..d47f2f3cd1 100644
--- a/doc/classes/PackedVector3Array.xml
+++ b/doc/classes/PackedVector3Array.xml
@@ -97,6 +97,13 @@
[b]Note:[/b] Vectors with [constant @GDScript.NAN] elements don't behave the same as other vectors. Therefore, the results from this method may not be accurate if NaNs are included.
</description>
</method>
+ <method name="get" qualifiers="const">
+ <return type="Vector3" />
+ <param index="0" name="index" type="int" />
+ <description>
+ Returns the [Vector3] at the given [param index] in the array. This is the same as using the [code][][/code] operator ([code]array[index][/code]).
+ </description>
+ </method>
<method name="has" qualifiers="const">
<return type="bool" />
<param index="0" name="value" type="Vector3" />
diff --git a/doc/classes/PackedVector4Array.xml b/doc/classes/PackedVector4Array.xml
index fd0cfeb74b..6dbfc7413d 100644
--- a/doc/classes/PackedVector4Array.xml
+++ b/doc/classes/PackedVector4Array.xml
@@ -96,6 +96,13 @@
[b]Note:[/b] Vectors with [constant @GDScript.NAN] elements don't behave the same as other vectors. Therefore, the results from this method may not be accurate if NaNs are included.
</description>
</method>
+ <method name="get" qualifiers="const">
+ <return type="Vector4" />
+ <param index="0" name="index" type="int" />
+ <description>
+ Returns the [Vector4] at the given [param index] in the array. This is the same as using the [code][][/code] operator ([code]array[index][/code]).
+ </description>
+ </method>
<method name="has" qualifiers="const">
<return type="bool" />
<param index="0" name="value" type="Vector4" />
diff --git a/doc/classes/ParticleProcessMaterial.xml b/doc/classes/ParticleProcessMaterial.xml
index 28c60194c8..742e0cc44c 100644
--- a/doc/classes/ParticleProcessMaterial.xml
+++ b/doc/classes/ParticleProcessMaterial.xml
@@ -90,6 +90,7 @@
<members>
<member name="alpha_curve" type="Texture2D" setter="set_alpha_curve" getter="get_alpha_curve">
The alpha value of each particle's color will be multiplied by this [CurveTexture] over its lifetime.
+ [b]Note:[/b] [member alpha_curve] multiplies the particle mesh's vertex colors. To have a visible effect on a [BaseMaterial3D], [member BaseMaterial3D.vertex_color_use_as_albedo] [i]must[/i] be [code]true[/code]. For a [ShaderMaterial], [code]ALBEDO *= COLOR.rgb;[/code] must be inserted in the shader's [code]fragment()[/code] function. Otherwise, [member alpha_curve] will have no visible effect.
</member>
<member name="angle_curve" type="Texture2D" setter="set_param_texture" getter="get_param_texture">
Each particle's rotation will be animated along this [CurveTexture].
@@ -193,7 +194,7 @@
</member>
<member name="emission_curve" type="Texture2D" setter="set_emission_curve" getter="get_emission_curve">
Each particle's color will be multiplied by this [CurveTexture] over its lifetime.
- [b]Note:[/b] This property won't have a visible effect unless the render material is marked as unshaded.
+ [b]Note:[/b] [member emission_curve] multiplies the particle mesh's vertex colors. To have a visible effect on a [BaseMaterial3D], [member BaseMaterial3D.vertex_color_use_as_albedo] [i]must[/i] be [code]true[/code]. For a [ShaderMaterial], [code]ALBEDO *= COLOR.rgb;[/code] must be inserted in the shader's [code]fragment()[/code] function. Otherwise, [member emission_curve] will have no visible effect.
</member>
<member name="emission_normal_texture" type="Texture2D" setter="set_emission_normal_texture" getter="get_emission_normal_texture">
Particle velocity and rotation will be set by sampling this texture at the same point as the [member emission_point_texture]. Used only in [constant EMISSION_SHAPE_DIRECTED_POINTS]. Can be created automatically from mesh or node by selecting "Create Emission Points from Mesh/Node" under the "Particles" tool in the toolbar.
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index 7b6d8d0cd3..af5ec41b60 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -2863,11 +2863,6 @@
</member>
<member name="rendering/shading/overrides/force_vertex_shading" type="bool" setter="" getter="" default="false">
If [code]true[/code], forces vertex shading for all rendering. This can increase performance a lot, but also reduces quality immensely. Can be used to optimize performance on low-end mobile devices.
- [b]Note:[/b] This setting currently has no effect, as vertex shading is not implemented yet.
- </member>
- <member name="rendering/shading/overrides/force_vertex_shading.mobile" type="bool" setter="" getter="" default="true">
- Lower-end override for [member rendering/shading/overrides/force_vertex_shading] on mobile devices, due to performance concerns or driver support.
- [b]Note:[/b] This setting currently has no effect, as vertex shading is not implemented yet.
</member>
<member name="rendering/textures/canvas_textures/default_texture_filter" type="int" setter="" getter="" default="1">
The default texture filtering mode to use on [CanvasItem]s.
diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp
index 65749323fd..c2d1784958 100644
--- a/drivers/gles3/rasterizer_scene_gles3.cpp
+++ b/drivers/gles3/rasterizer_scene_gles3.cpp
@@ -4125,6 +4125,9 @@ RasterizerSceneGLES3::RasterizerSceneGLES3() {
global_defines += "\n#define MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS " + itos(MAX_DIRECTIONAL_LIGHTS) + "\n";
global_defines += "\n#define MAX_FORWARD_LIGHTS " + itos(config->max_lights_per_object) + "u\n";
global_defines += "\n#define MAX_ROUGHNESS_LOD " + itos(sky_globals.roughness_layers - 1) + ".0\n";
+ if (config->force_vertex_shading) {
+ global_defines += "\n#define USE_VERTEX_LIGHTING\n";
+ }
material_storage->shaders.scene_shader.initialize(global_defines);
scene_globals.shader_default_version = material_storage->shaders.scene_shader.version_create();
material_storage->shaders.scene_shader.version_bind_shader(scene_globals.shader_default_version, SceneShaderGLES3::MODE_COLOR);
diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl
index 393ba014c6..9ce10a4488 100644
--- a/drivers/gles3/shaders/scene.glsl
+++ b/drivers/gles3/shaders/scene.glsl
@@ -248,6 +248,175 @@ uniform lowp uint directional_shadow_index;
#endif // !(defined(ADDITIVE_OMNI) || defined(ADDITIVE_SPOT))
#endif // USE_ADDITIVE_LIGHTING
+#ifdef USE_VERTEX_LIGHTING
+
+out vec3 diffuse_light_interp;
+out vec3 specular_light_interp;
+
+#ifdef USE_ADDITIVE_LIGHTING
+out vec3 additive_diffuse_light_interp;
+out vec3 additive_specular_light_interp;
+#endif // USE_ADDITIVE_LIGHTING
+
+// Directional light data.
+#if !defined(DISABLE_LIGHT_DIRECTIONAL) || (!defined(ADDITIVE_OMNI) && !defined(ADDITIVE_SPOT) && defined(USE_ADDITIVE_LIGHTING))
+
+struct DirectionalLightData {
+ mediump vec3 direction;
+ mediump float energy;
+ mediump vec3 color;
+ mediump float size;
+ lowp uint unused;
+ lowp uint bake_mode;
+ mediump float shadow_opacity;
+ mediump float specular;
+};
+
+layout(std140) uniform DirectionalLights { // ubo:7
+ DirectionalLightData directional_lights[MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS];
+};
+#endif // !DISABLE_LIGHT_DIRECTIONAL
+
+// Omni and spot light data.
+#if !defined(DISABLE_LIGHT_OMNI) || !defined(DISABLE_LIGHT_SPOT) || (defined(ADDITIVE_OMNI) || defined(ADDITIVE_SPOT) && defined(USE_ADDITIVE_LIGHTING))
+
+struct LightData { // This structure needs to be as packed as possible.
+ highp vec3 position;
+ highp float inv_radius;
+
+ mediump vec3 direction;
+ highp float size;
+
+ mediump vec3 color;
+ mediump float attenuation;
+
+ mediump float cone_attenuation;
+ mediump float cone_angle;
+ mediump float specular_amount;
+ mediump float shadow_opacity;
+
+ lowp vec3 pad;
+ lowp uint bake_mode;
+};
+
+#if !defined(DISABLE_LIGHT_OMNI) || defined(ADDITIVE_OMNI)
+layout(std140) uniform OmniLightData { // ubo:5
+ LightData omni_lights[MAX_LIGHT_DATA_STRUCTS];
+};
+#ifdef BASE_PASS
+uniform uint omni_light_indices[MAX_FORWARD_LIGHTS];
+uniform uint omni_light_count;
+#endif // BASE_PASS
+#endif // DISABLE_LIGHT_OMNI
+
+#if !defined(DISABLE_LIGHT_SPOT) || defined(ADDITIVE_SPOT)
+layout(std140) uniform SpotLightData { // ubo:6
+ LightData spot_lights[MAX_LIGHT_DATA_STRUCTS];
+};
+#ifdef BASE_PASS
+uniform uint spot_light_indices[MAX_FORWARD_LIGHTS];
+uniform uint spot_light_count;
+#endif // BASE_PASS
+#endif // DISABLE_LIGHT_SPOT
+#endif // !defined(DISABLE_LIGHT_OMNI) || !defined(DISABLE_LIGHT_SPOT) || (defined(ADDITIVE_OMNI) || defined(ADDITIVE_SPOT) && defined(USE_ADDITIVE_LIGHTING))
+
+#ifdef USE_ADDITIVE_LIGHTING
+#ifdef ADDITIVE_OMNI
+uniform lowp uint omni_light_index;
+#endif
+#ifdef ADDITIVE_SPOT
+uniform lowp uint spot_light_index;
+#endif
+#endif // USE_ADDITIVE_LIGHTING
+
+#if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) && defined(USE_VERTEX_LIGHTING)
+
+// Eyeballed approximation of `exp2(15.0 * (1.0 - roughness) + 1.0) * 0.25`.
+// Uses slightly more FMA instructions (2x rate) to avoid special instructions (0.25x rate).
+// Range is reduced to [0.64,4977] from [068,2,221,528] which makes mediump feasible for the rest of the shader.
+mediump float roughness_to_shininess(mediump float roughness) {
+ mediump float r = 1.2 - roughness;
+ mediump float r2 = r * r;
+ return r * r2 * r2 * 2000.0;
+}
+
+void light_compute(vec3 N, vec3 L, vec3 V, vec3 light_color, bool is_directional, float roughness,
+ inout vec3 diffuse_light, inout vec3 specular_light) {
+ float NdotL = min(dot(N, L), 1.0);
+ float cNdotL = max(NdotL, 0.0); // clamped NdotL
+
+#if defined(DIFFUSE_LAMBERT_WRAP)
+ // Energy conserving lambert wrap shader.
+ // https://web.archive.org/web/20210228210901/http://blog.stevemcauley.com/2011/12/03/energy-conserving-wrapped-diffuse/
+ float diffuse_brdf_NL = max(0.0, (cNdotL + roughness) / ((1.0 + roughness) * (1.0 + roughness))) * (1.0 / M_PI);
+#else
+ // lambert
+ float diffuse_brdf_NL = cNdotL * (1.0 / M_PI);
+#endif
+
+ diffuse_light += light_color * diffuse_brdf_NL;
+
+#if !defined(SPECULAR_DISABLED)
+ float specular_brdf_NL = 0.0;
+ // Normalized blinn always unless disabled.
+ vec3 H = normalize(V + L);
+ float cNdotH = clamp(dot(N, H), 0.0, 1.0);
+ float shininess = roughness_to_shininess(roughness);
+ float blinn = pow(cNdotH, shininess);
+ blinn *= (shininess + 2.0) * (1.0 / (8.0 * M_PI)) * cNdotL;
+ specular_brdf_NL = blinn;
+ specular_light += specular_brdf_NL * light_color;
+#endif
+}
+
+float get_omni_spot_attenuation(float distance, float inv_range, float decay) {
+ float nd = distance * inv_range;
+ nd *= nd;
+ nd *= nd; // nd^4
+ nd = max(1.0 - nd, 0.0);
+ nd *= nd; // nd^2
+ return nd * pow(max(distance, 0.0001), -decay);
+}
+
+#if !defined(DISABLE_LIGHT_OMNI) || (defined(ADDITIVE_OMNI) && defined(USE_ADDITIVE_LIGHTING))
+void light_process_omni(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, float roughness,
+ inout vec3 diffuse_light, inout vec3 specular_light) {
+ vec3 light_rel_vec = omni_lights[idx].position - vertex;
+ float light_length = length(light_rel_vec);
+ float omni_attenuation = get_omni_spot_attenuation(light_length, omni_lights[idx].inv_radius, omni_lights[idx].attenuation);
+ vec3 color = omni_lights[idx].color * omni_attenuation; // No light shaders here, so combine.
+
+ light_compute(normal, normalize(light_rel_vec), eye_vec, color, false, roughness,
+ diffuse_light,
+ specular_light);
+}
+#endif // !defined(DISABLE_LIGHT_OMNI) || (defined(ADDITIVE_OMNI) && defined(USE_ADDITIVE_LIGHTING))
+
+#if !defined(DISABLE_LIGHT_SPOT) || (defined(ADDITIVE_SPOT) && defined(USE_ADDITIVE_LIGHTING))
+void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, float roughness,
+ inout vec3 diffuse_light,
+ inout vec3 specular_light) {
+ vec3 light_rel_vec = spot_lights[idx].position - vertex;
+ float light_length = length(light_rel_vec);
+ float spot_attenuation = get_omni_spot_attenuation(light_length, spot_lights[idx].inv_radius, spot_lights[idx].attenuation);
+ vec3 spot_dir = spot_lights[idx].direction;
+ float scos = max(dot(-normalize(light_rel_vec), spot_dir), spot_lights[idx].cone_angle);
+ float spot_rim = max(0.0001, (1.0 - scos) / (1.0 - spot_lights[idx].cone_angle));
+
+ mediump float cone_attenuation = spot_lights[idx].cone_attenuation;
+ spot_attenuation *= 1.0 - pow(spot_rim, cone_attenuation);
+
+ vec3 color = spot_lights[idx].color * spot_attenuation;
+
+ light_compute(normal, normalize(light_rel_vec), eye_vec, color, false, roughness,
+ diffuse_light, specular_light);
+}
+#endif // !defined(DISABLE_LIGHT_SPOT) || (defined(ADDITIVE_SPOT) && defined(USE_ADDITIVE_LIGHTING))
+
+#endif // !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) && defined(USE_VERTEX_LIGHTING)
+
+#endif // USE_VERTEX_LIGHTING
+
#ifdef USE_MULTIVIEW
layout(std140) uniform MultiviewData { // ubo:8
highp mat4 projection_matrix_view[MAX_VIEWS];
@@ -540,8 +709,65 @@ void main() {
gl_Position.z = 0.00001;
gl_Position.w = 1.0;
#endif
-}
+#ifdef USE_VERTEX_LIGHTING
+#if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED)
+#ifdef USE_MULTIVIEW
+ vec3 view = -normalize(vertex_interp - eye_offset);
+#else
+ vec3 view = -normalize(vertex_interp);
+#endif
+ diffuse_light_interp = vec3(0.0);
+ specular_light_interp = vec3(0.0);
+#ifdef BASE_PASS
+#ifndef DISABLE_LIGHT_DIRECTIONAL
+ for (uint i = uint(0); i < scene_data.directional_light_count; i++) {
+ light_compute(normal_interp, normalize(directional_lights[i].direction), normalize(view), directional_lights[i].color * directional_lights[i].energy, true, roughness,
+ diffuse_light_interp.rgb,
+ specular_light_interp.rgb);
+ }
+#endif // !DISABLE_LIGHT_DIRECTIONAL
+
+#ifndef DISABLE_LIGHT_OMNI
+ for (uint i = 0u; i < omni_light_count; i++) {
+ light_process_omni(omni_light_indices[i], vertex_interp, view, normal_interp, roughness,
+ diffuse_light_interp.rgb, specular_light_interp.rgb);
+ }
+#endif // !DISABLE_LIGHT_OMNI
+
+#ifndef DISABLE_LIGHT_SPOT
+ for (uint i = 0u; i < spot_light_count; i++) {
+ light_process_spot(spot_light_indices[i], vertex_interp, view, normal_interp, roughness,
+ diffuse_light_interp.rgb, specular_light_interp.rgb);
+ }
+#endif // !DISABLE_LIGHT_SPOT
+#endif // BASE_PASS
+
+/* ADDITIVE LIGHTING PASS */
+#ifdef USE_ADDITIVE_LIGHTING
+ additive_diffuse_light_interp = vec3(0.0);
+ additive_specular_light_interp = vec3(0.0);
+#if !defined(ADDITIVE_OMNI) && !defined(ADDITIVE_SPOT)
+
+ light_compute(normal_interp, normalize(directional_lights[directional_shadow_index].direction), normalize(view), directional_lights[directional_shadow_index].color * directional_lights[directional_shadow_index].energy, true, roughness,
+ additive_diffuse_light_interp.rgb,
+ additive_specular_light_interp.rgb);
+#endif // !defined(ADDITIVE_OMNI) && !defined(ADDITIVE_SPOT)
+
+#ifdef ADDITIVE_OMNI
+ light_process_omni(omni_light_index, vertex_interp, view, normal_interp, roughness,
+ additive_diffuse_light_interp.rgb, additive_specular_light_interp.rgb);
+#endif // ADDITIVE_OMNI
+
+#ifdef ADDITIVE_SPOT
+ light_process_spot(spot_light_index, vertex_interp, view, normal_interp, roughness,
+ additive_diffuse_light_interp.rgb, additive_specular_light_interp.rgb);
+#endif // ADDITIVE_SPOT
+
+#endif // USE_ADDITIVE_LIGHTING
+#endif // !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED)
+#endif // USE_VERTEX_LIGHTING
+}
/* clang-format off */
#[fragment]
@@ -758,6 +984,16 @@ multiview_data;
#define LIGHT_BAKE_DYNAMIC 2u
#ifndef MODE_RENDER_DEPTH
+#ifdef USE_VERTEX_LIGHTING
+in vec3 diffuse_light_interp;
+in vec3 specular_light_interp;
+
+#ifdef USE_ADDITIVE_LIGHTING
+in vec3 additive_diffuse_light_interp;
+in vec3 additive_specular_light_interp;
+#endif // USE_ADDITIVE_LIGHTING
+#endif // USE_VERTEX_LIGHTING
+
// Directional light data.
#if !defined(DISABLE_LIGHT_DIRECTIONAL) || (!defined(ADDITIVE_OMNI) && !defined(ADDITIVE_SPOT))
@@ -809,22 +1045,22 @@ struct LightData { // This structure needs to be as packed as possible.
layout(std140) uniform OmniLightData { // ubo:5
LightData omni_lights[MAX_LIGHT_DATA_STRUCTS];
};
-#ifdef BASE_PASS
+#if defined(BASE_PASS) && !defined(USE_VERTEX_LIGHTING)
uniform uint omni_light_indices[MAX_FORWARD_LIGHTS];
uniform uint omni_light_count;
-#endif // BASE_PASS
-#endif // DISABLE_LIGHT_OMNI
+#endif // defined(BASE_PASS) && !defined(USE_VERTEX_LIGHTING)
+#endif // !defined(DISABLE_LIGHT_OMNI) || defined(ADDITIVE_OMNI)
#if !defined(DISABLE_LIGHT_SPOT) || defined(ADDITIVE_SPOT)
layout(std140) uniform SpotLightData { // ubo:6
LightData spot_lights[MAX_LIGHT_DATA_STRUCTS];
};
-#ifdef BASE_PASS
+#if defined(BASE_PASS) && !defined(USE_VERTEX_LIGHTING)
uniform uint spot_light_indices[MAX_FORWARD_LIGHTS];
uniform uint spot_light_count;
-#endif // BASE_PASS
-#endif // DISABLE_LIGHT_SPOT
-#endif // !defined(DISABLE_LIGHT_OMNI) || !defined(DISABLE_LIGHT_SPOT)
+#endif // defined(BASE_PASS) && !defined(USE_VERTEX_LIGHTING)
+#endif // !defined(DISABLE_LIGHT_SPOT) || defined(ADDITIVE_SPOT)
+#endif // !defined(DISABLE_LIGHT_OMNI) || !defined(DISABLE_LIGHT_SPOT) || defined(ADDITIVE_OMNI) || defined(ADDITIVE_SPOT)
#ifdef USE_ADDITIVE_LIGHTING
#ifdef ADDITIVE_OMNI
@@ -985,6 +1221,8 @@ vec3 F0(float metallic, float specular, vec3 albedo) {
return mix(vec3(dielectric), albedo, vec3(metallic));
}
#ifndef MODE_RENDER_DEPTH
+
+#ifndef USE_VERTEX_LIGHTING
#if !defined(DISABLE_LIGHT_DIRECTIONAL) || !defined(DISABLE_LIGHT_OMNI) || !defined(DISABLE_LIGHT_SPOT) || defined(USE_ADDITIVE_LIGHTING)
float D_GGX(float cos_theta_m, float alpha) {
@@ -1284,6 +1522,7 @@ void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 f
#endif // !defined(DISABLE_LIGHT_SPOT) || defined(ADDITIVE_SPOT)
#endif // !defined(DISABLE_LIGHT_DIRECTIONAL) || !defined(DISABLE_LIGHT_OMNI) || !defined(DISABLE_LIGHT_SPOT)
+#endif // !USE_VERTEX_LIGHTING
vec4 fog_process(vec3 vertex) {
vec3 fog_color = scene_data.fog_light_color;
@@ -1859,9 +2098,13 @@ void main() {
specular_light *= env.x * f0 + env.y * clamp(50.0 * f0.g, metallic, 1.0);
#endif
}
-
#endif // !AMBIENT_LIGHT_DISABLED
+#ifdef USE_VERTEX_LIGHTING
+ specular_light += specular_light_interp * f0;
+ diffuse_light += diffuse_light_interp;
+#else
+
#ifndef DISABLE_LIGHT_DIRECTIONAL
for (uint i = uint(0); i < scene_data.directional_light_count; i++) {
#if defined(USE_LIGHTMAP) && !defined(DISABLE_LIGHTMAP)
@@ -1944,6 +2187,7 @@ void main() {
diffuse_light, specular_light);
}
#endif // !DISABLE_LIGHT_SPOT
+#endif // !USE_VERTEX_LIGHTING
#endif // BASE_PASS
#endif // !MODE_UNSHADED
@@ -1993,7 +2237,6 @@ void main() {
#else
diffuse_light *= albedo;
-
diffuse_light *= 1.0 - metallic;
ambient_light *= 1.0 - metallic;
@@ -2024,6 +2267,11 @@ void main() {
diffuse_light = vec3(0.0);
specular_light = vec3(0.0);
+#ifdef USE_VERTEX_LIGHTING
+ diffuse_light = additive_diffuse_light_interp;
+ specular_light = additive_specular_light_interp * f0;
+#endif // USE_VERTEX_LIGHTING
+
#if !defined(ADDITIVE_OMNI) && !defined(ADDITIVE_SPOT)
#ifndef SHADOWS_DISABLED
@@ -2137,6 +2385,8 @@ void main() {
#else
float directional_shadow = 1.0f;
#endif // SHADOWS_DISABLED
+
+#ifndef USE_VERTEX_LIGHTING
light_compute(normal, normalize(directional_lights[directional_shadow_index].direction), normalize(view), directional_lights[directional_shadow_index].size, directional_lights[directional_shadow_index].color * directional_lights[directional_shadow_index].energy, true, directional_shadow, f0, roughness, metallic, 1.0, albedo, alpha,
#ifdef LIGHT_BACKLIGHT_USED
backlight,
@@ -2153,6 +2403,11 @@ void main() {
#endif
diffuse_light,
specular_light);
+#else
+ // Just apply shadows to vertex lighting.
+ diffuse_light *= directional_shadow;
+ specular_light *= directional_shadow;
+#endif // !USE_VERTEX_LIGHTING
#endif // !defined(ADDITIVE_OMNI) && !defined(ADDITIVE_SPOT)
#ifdef ADDITIVE_OMNI
@@ -2162,6 +2417,8 @@ void main() {
omni_shadow = texture(omni_shadow_texture, vec4(light_ray, 1.0 - length(light_ray) * omni_lights[omni_light_index].inv_radius));
omni_shadow = mix(1.0, omni_shadow, omni_lights[omni_light_index].shadow_opacity);
#endif // SHADOWS_DISABLED
+
+#ifndef USE_VERTEX_LIGHTING
light_process_omni(omni_light_index, vertex, view, normal, f0, roughness, metallic, omni_shadow, albedo, alpha,
#ifdef LIGHT_BACKLIGHT_USED
backlight,
@@ -2177,6 +2434,11 @@ void main() {
binormal, tangent, anisotropy,
#endif
diffuse_light, specular_light);
+#else
+ // Just apply shadows to vertex lighting.
+ diffuse_light *= omni_shadow;
+ specular_light *= omni_shadow;
+#endif // !USE_VERTEX_LIGHTING
#endif // ADDITIVE_OMNI
#ifdef ADDITIVE_SPOT
@@ -2185,6 +2447,8 @@ void main() {
spot_shadow = sample_shadow(spot_shadow_texture, positional_shadows[positional_shadow_index].shadow_atlas_pixel_size, shadow_coord);
spot_shadow = mix(1.0, spot_shadow, spot_lights[spot_light_index].shadow_opacity);
#endif // SHADOWS_DISABLED
+
+#ifndef USE_VERTEX_LIGHTING
light_process_spot(spot_light_index, vertex, view, normal, f0, roughness, metallic, spot_shadow, albedo, alpha,
#ifdef LIGHT_BACKLIGHT_USED
backlight,
@@ -2201,6 +2465,11 @@ void main() {
binormal, anisotropy,
#endif
diffuse_light, specular_light);
+#else
+ // Just apply shadows to vertex lighting.
+ diffuse_light *= spot_shadow;
+ specular_light *= spot_shadow;
+#endif // !USE_VERTEX_LIGHTING
#endif // ADDITIVE_SPOT
diff --git a/drivers/gles3/storage/config.cpp b/drivers/gles3/storage/config.cpp
index 0100719151..209b7dd7d2 100644
--- a/drivers/gles3/storage/config.cpp
+++ b/drivers/gles3/storage/config.cpp
@@ -178,7 +178,7 @@ Config::Config() {
}
#endif
- force_vertex_shading = false; //GLOBAL_GET("rendering/quality/shading/force_vertex_shading");
+ force_vertex_shading = GLOBAL_GET("rendering/shading/overrides/force_vertex_shading");
use_nearest_mip_filter = GLOBAL_GET("rendering/textures/default_filters/use_nearest_mipmap_filter");
use_depth_prepass = bool(GLOBAL_GET("rendering/driver/depth_prepass/enable"));
diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp
index c29c741c2a..5dca149d99 100644
--- a/drivers/gles3/storage/material_storage.cpp
+++ b/drivers/gles3/storage/material_storage.cpp
@@ -1368,6 +1368,10 @@ MaterialStorage::MaterialStorage() {
actions.render_mode_defines["ambient_light_disabled"] = "#define AMBIENT_LIGHT_DISABLED\n";
actions.render_mode_defines["shadow_to_opacity"] = "#define USE_SHADOW_TO_OPACITY\n";
actions.render_mode_defines["unshaded"] = "#define MODE_UNSHADED\n";
+ if (!GLES3::Config::get_singleton()->force_vertex_shading) {
+ // If forcing vertex shading, this will be defined already.
+ actions.render_mode_defines["vertex_lighting"] = "#define USE_VERTEX_LIGHTING\n";
+ }
actions.render_mode_defines["fog_disabled"] = "#define FOG_DISABLED\n";
actions.default_filter = ShaderLanguage::FILTER_LINEAR_MIPMAP;
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp
index d277ba2f6d..63f86607e5 100644
--- a/editor/animation_track_editor.cpp
+++ b/editor/animation_track_editor.cpp
@@ -39,6 +39,7 @@
#include "editor/editor_string_names.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/gui/editor_spin_slider.h"
+#include "editor/gui/editor_validation_panel.h"
#include "editor/gui/scene_tree_editor.h"
#include "editor/inspector_dock.h"
#include "editor/multi_node_edit.h"
@@ -48,6 +49,7 @@
#include "scene/animation/animation_player.h"
#include "scene/animation/tween.h"
#include "scene/gui/check_box.h"
+#include "scene/gui/color_picker.h"
#include "scene/gui/grid_container.h"
#include "scene/gui/option_button.h"
#include "scene/gui/panel_container.h"
@@ -1467,7 +1469,7 @@ void AnimationTimelineEdit::_notification(int p_what) {
case NOTIFICATION_DRAW: {
int key_range = get_size().width - get_buttons_width() - get_name_limit();
- if (!animation.is_valid()) {
+ if (animation.is_null()) {
return;
}
@@ -1522,6 +1524,18 @@ void AnimationTimelineEdit::_notification(int p_what) {
}
}
+ PackedStringArray markers = animation->get_marker_names();
+ if (markers.size() > 0) {
+ float min_marker = animation->get_marker_time(markers[0]);
+ float max_marker = animation->get_marker_time(markers[markers.size() - 1]);
+ if (min_marker < time_min) {
+ time_min = min_marker;
+ }
+ if (max_marker > time_max) {
+ time_max = max_marker;
+ }
+ }
+
float extra = (zoomw / scale) * 0.5;
time_max += extra;
@@ -1701,7 +1715,7 @@ void AnimationTimelineEdit::set_zoom(Range *p_zoom) {
}
void AnimationTimelineEdit::auto_fit() {
- if (!animation.is_valid()) {
+ if (animation.is_null()) {
return;
}
@@ -1780,7 +1794,7 @@ void AnimationTimelineEdit::update_play_position() {
}
void AnimationTimelineEdit::update_values() {
- if (!animation.is_valid() || editing) {
+ if (animation.is_null() || editing) {
return;
}
@@ -1792,6 +1806,7 @@ void AnimationTimelineEdit::update_values() {
time_icon->set_tooltip_text(TTR("Animation length (frames)"));
if (track_edit) {
track_edit->editor->_update_key_edit();
+ track_edit->editor->marker_edit->_update_key_edit();
}
} else {
length->set_value(animation->get_length());
@@ -1821,7 +1836,7 @@ void AnimationTimelineEdit::update_values() {
}
void AnimationTimelineEdit::_play_position_draw() {
- if (!animation.is_valid() || play_position_pos < 0) {
+ if (animation.is_null() || play_position_pos < 0) {
return;
}
@@ -1972,6 +1987,7 @@ AnimationTimelineEdit::AnimationTimelineEdit() {
Control *expander = memnew(Control);
expander->set_h_size_flags(SIZE_EXPAND_FILL);
+ expander->set_mouse_filter(MOUSE_FILTER_IGNORE);
len_hb->add_child(expander);
time_icon = memnew(TextureRect);
time_icon->set_v_size_flags(SIZE_SHRINK_CENTER);
@@ -2124,6 +2140,62 @@ void AnimationTrackEdit::_notification(int p_what) {
draw_line(Point2(limit, 0), Point2(limit, get_size().height), h_line_color, Math::round(EDSCALE));
}
+ // Marker sections.
+
+ {
+ float scale = timeline->get_zoom_scale();
+ int limit_end = get_size().width - timeline->get_buttons_width();
+
+ PackedStringArray section = editor->get_selected_section();
+ if (section.size() == 2) {
+ StringName start_marker = section[0];
+ StringName end_marker = section[1];
+ double start_time = animation->get_marker_time(start_marker);
+ double end_time = animation->get_marker_time(end_marker);
+
+ // When AnimationPlayer is playing, don't move the preview rect, so it still indicates the playback section.
+ AnimationPlayer *player = AnimationPlayerEditor::get_singleton()->get_player();
+ if (editor->is_marker_moving_selection() && !(player && player->is_playing())) {
+ start_time += editor->get_marker_moving_selection_offset();
+ end_time += editor->get_marker_moving_selection_offset();
+ }
+
+ if (start_time < animation->get_length() && end_time >= 0) {
+ float start_ofs = MAX(0, start_time) - timeline->get_value();
+ float end_ofs = MIN(animation->get_length(), end_time) - timeline->get_value();
+ start_ofs = start_ofs * scale + limit;
+ end_ofs = end_ofs * scale + limit;
+ start_ofs = MAX(start_ofs, limit);
+ end_ofs = MIN(end_ofs, limit_end);
+ Rect2 rect;
+ rect.set_position(Vector2(start_ofs, 0));
+ rect.set_size(Vector2(end_ofs - start_ofs, get_size().height));
+
+ draw_rect(rect, Color(1, 0.1, 0.1, 0.2));
+ }
+ }
+ }
+
+ // Marker overlays.
+
+ {
+ float scale = timeline->get_zoom_scale();
+ PackedStringArray markers = animation->get_marker_names();
+ for (const StringName marker : markers) {
+ double time = animation->get_marker_time(marker);
+ if (editor->is_marker_selected(marker) && editor->is_marker_moving_selection()) {
+ time += editor->get_marker_moving_selection_offset();
+ }
+ if (time >= 0) {
+ float offset = time - timeline->get_value();
+ offset = offset * scale + limit;
+ Color marker_color = animation->get_marker_color(marker);
+ marker_color.a = 0.2;
+ draw_line(Point2(offset, 0), Point2(offset, get_size().height), marker_color);
+ }
+ }
+ }
+
// Keyframes.
draw_bg(limit, get_size().width - timeline->get_buttons_width() - outer_margin);
@@ -2352,7 +2424,7 @@ void AnimationTrackEdit::_notification(int p_what) {
}
int AnimationTrackEdit::get_key_height() const {
- if (!animation.is_valid()) {
+ if (animation.is_null()) {
return 0;
}
@@ -2360,7 +2432,7 @@ int AnimationTrackEdit::get_key_height() const {
}
Rect2 AnimationTrackEdit::get_key_rect(int p_index, float p_pixels_sec) {
- if (!animation.is_valid()) {
+ if (animation.is_null()) {
return Rect2();
}
Rect2 rect = Rect2(-type_icon->get_width() / 2, 0, type_icon->get_width(), get_size().height);
@@ -2399,7 +2471,7 @@ void AnimationTrackEdit::draw_key_link(int p_index, float p_pixels_sec, int p_x,
}
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()) {
+ if (animation.is_null()) {
return;
}
@@ -2573,7 +2645,7 @@ void AnimationTrackEdit::set_editor(AnimationTrackEditor *p_editor) {
}
void AnimationTrackEdit::_play_position_draw() {
- if (!animation.is_valid() || play_position_pos < 0) {
+ if (animation.is_null() || play_position_pos < 0) {
return;
}
@@ -3522,6 +3594,64 @@ void AnimationTrackEditGroup::_notification(int p_what) {
draw_style_box(stylebox_header, Rect2(Point2(), get_size()));
+ int limit = timeline->get_name_limit();
+
+ // Section preview.
+
+ {
+ float scale = timeline->get_zoom_scale();
+ int limit_end = get_size().width - timeline->get_buttons_width();
+
+ PackedStringArray section = editor->get_selected_section();
+ if (section.size() == 2) {
+ StringName start_marker = section[0];
+ StringName end_marker = section[1];
+ double start_time = editor->get_current_animation()->get_marker_time(start_marker);
+ double end_time = editor->get_current_animation()->get_marker_time(end_marker);
+
+ // When AnimationPlayer is playing, don't move the preview rect, so it still indicates the playback section.
+ AnimationPlayer *player = AnimationPlayerEditor::get_singleton()->get_player();
+ if (editor->is_marker_moving_selection() && !(player && player->is_playing())) {
+ start_time += editor->get_marker_moving_selection_offset();
+ end_time += editor->get_marker_moving_selection_offset();
+ }
+
+ if (start_time < editor->get_current_animation()->get_length() && end_time >= 0) {
+ float start_ofs = MAX(0, start_time) - timeline->get_value();
+ float end_ofs = MIN(editor->get_current_animation()->get_length(), end_time) - timeline->get_value();
+ start_ofs = start_ofs * scale + limit;
+ end_ofs = end_ofs * scale + limit;
+ start_ofs = MAX(start_ofs, limit);
+ end_ofs = MIN(end_ofs, limit_end);
+ Rect2 rect;
+ rect.set_position(Vector2(start_ofs, 0));
+ rect.set_size(Vector2(end_ofs - start_ofs, get_size().height));
+
+ draw_rect(rect, Color(1, 0.1, 0.1, 0.2));
+ }
+ }
+ }
+
+ // Marker overlays.
+
+ {
+ float scale = timeline->get_zoom_scale();
+ PackedStringArray markers = editor->get_current_animation()->get_marker_names();
+ for (const StringName marker : markers) {
+ double time = editor->get_current_animation()->get_marker_time(marker);
+ if (editor->is_marker_selected(marker) && editor->is_marker_moving_selection()) {
+ time += editor->get_marker_moving_selection_offset();
+ }
+ if (time >= 0) {
+ float offset = time - timeline->get_value();
+ offset = offset * scale + limit;
+ Color marker_color = editor->get_current_animation()->get_marker_color(marker);
+ marker_color.a = 0.2;
+ draw_line(Point2(offset, 0), Point2(offset, get_size().height), marker_color);
+ }
+ }
+ }
+
draw_line(Point2(), Point2(get_size().width, 0), h_line_color, Math::round(EDSCALE));
draw_line(Point2(timeline->get_name_limit(), 0), Point2(timeline->get_name_limit(), get_size().height), v_line_color, Math::round(EDSCALE));
draw_line(Point2(get_size().width - timeline->get_buttons_width() - outer_margin, 0), Point2(get_size().width - timeline->get_buttons_width() - outer_margin, get_size().height), v_line_color, Math::round(EDSCALE));
@@ -3590,6 +3720,10 @@ void AnimationTrackEditGroup::set_root(Node *p_root) {
queue_redraw();
}
+void AnimationTrackEditGroup::set_editor(AnimationTrackEditor *p_editor) {
+ editor = p_editor;
+}
+
void AnimationTrackEditGroup::_zoom_changed() {
queue_redraw();
}
@@ -3623,6 +3757,9 @@ void AnimationTrackEditor::set_animation(const Ref<Animation> &p_anim, bool p_re
read_only = p_read_only;
timeline->set_animation(p_anim, read_only);
+ marker_edit->set_animation(p_anim, read_only);
+ marker_edit->set_play_position(timeline->get_play_position());
+
_cancel_bezier_edit();
_update_tracks();
@@ -3873,6 +4010,7 @@ void AnimationTrackEditor::_track_grab_focus(int p_track) {
void AnimationTrackEditor::set_anim_pos(float p_pos) {
timeline->set_play_position(p_pos);
+ marker_edit->set_play_position(p_pos);
for (int i = 0; i < track_edits.size(); i++) {
track_edits[i]->set_play_position(p_pos);
}
@@ -4043,7 +4181,7 @@ void AnimationTrackEditor::insert_transform_key(Node3D *p_node, const String &p_
if (!keying) {
return;
}
- if (!animation.is_valid()) {
+ if (animation.is_null()) {
return;
}
@@ -4083,7 +4221,7 @@ bool AnimationTrackEditor::has_track(Node3D *p_node, const String &p_sub, const
if (!keying) {
return false;
}
- if (!animation.is_valid()) {
+ if (animation.is_null()) {
return false;
}
@@ -4230,6 +4368,22 @@ void AnimationTrackEditor::insert_node_value_key(Node *p_node, const String &p_p
_query_insert(id);
}
+PackedStringArray AnimationTrackEditor::get_selected_section() const {
+ return marker_edit->get_selected_section();
+}
+
+bool AnimationTrackEditor::is_marker_selected(const StringName &p_marker) const {
+ return marker_edit->is_marker_selected(p_marker);
+}
+
+bool AnimationTrackEditor::is_marker_moving_selection() const {
+ return marker_edit->is_moving_selection();
+}
+
+float AnimationTrackEditor::get_marker_moving_selection_offset() const {
+ return marker_edit->get_moving_selection_offset();
+}
+
void AnimationTrackEditor::insert_value_key(const String &p_property, bool p_advance) {
EditorSelectionHistory *history = EditorNode::get_singleton()->get_editor_selection_history();
@@ -4316,7 +4470,7 @@ void AnimationTrackEditor::_confirm_insert_list() {
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_COND_V(animation.is_null(), PropertyInfo());
ERR_FAIL_INDEX_V(p_idx, animation->get_track_count(), PropertyInfo());
if (!root) {
@@ -4769,6 +4923,7 @@ void AnimationTrackEditor::_update_tracks() {
g->set_root(root);
g->set_tooltip_text(tooltip);
g->set_timeline(timeline);
+ g->set_editor(this);
groups.push_back(g);
VBoxContainer *vb = memnew(VBoxContainer);
vb->add_theme_constant_override("separation", 0);
@@ -4860,12 +5015,13 @@ void AnimationTrackEditor::_snap_mode_changed(int p_mode) {
if (key_edit) {
key_edit->set_use_fps(use_fps);
}
+ marker_edit->set_use_fps(use_fps);
step->set_step(use_fps ? FPS_DECIMAL : SECOND_DECIMAL);
_update_step_spinbox();
}
void AnimationTrackEditor::_update_step_spinbox() {
- if (!animation.is_valid()) {
+ if (animation.is_null()) {
return;
}
step->set_block_signals(true);
@@ -4978,6 +5134,7 @@ void AnimationTrackEditor::_notification(int p_what) {
void AnimationTrackEditor::_update_scroll(double) {
_redraw_tracks();
_redraw_groups();
+ marker_edit->queue_redraw();
}
void AnimationTrackEditor::_update_step(double p_new_step) {
@@ -5253,6 +5410,8 @@ void AnimationTrackEditor::_timeline_value_changed(double) {
bezier_edit->queue_redraw();
bezier_edit->update_play_position();
+
+ marker_edit->update_play_position();
}
int AnimationTrackEditor::_get_track_selected() {
@@ -5445,6 +5604,8 @@ void AnimationTrackEditor::_key_selected(int p_key, bool p_single, int p_track)
_redraw_tracks();
_update_key_edit();
+
+ marker_edit->_clear_selection(marker_edit->is_selection_active());
}
void AnimationTrackEditor::_key_deselected(int p_key, int p_track) {
@@ -5513,7 +5674,7 @@ void AnimationTrackEditor::_clear_selection(bool p_update) {
void AnimationTrackEditor::_update_key_edit() {
_clear_key_edit();
- if (!animation.is_valid()) {
+ if (animation.is_null()) {
return;
}
@@ -5600,6 +5761,8 @@ void AnimationTrackEditor::_select_at_anim(const Ref<Animation> &p_anim, int p_t
selection.insert(sk, ki);
_update_key_edit();
+
+ marker_edit->_clear_selection(marker_edit->is_selection_active());
}
void AnimationTrackEditor::_move_selection_commit() {
@@ -7311,6 +7474,15 @@ AnimationTrackEditor::AnimationTrackEditor() {
box_selection_container->set_clip_contents(true);
timeline_vbox->add_child(box_selection_container);
+ marker_edit = memnew(AnimationMarkerEdit);
+ timeline->get_child(0)->add_child(marker_edit);
+ marker_edit->set_editor(this);
+ marker_edit->set_timeline(timeline);
+ marker_edit->set_h_size_flags(SIZE_EXPAND_FILL);
+ marker_edit->set_anchors_and_offsets_preset(Control::LayoutPreset::PRESET_FULL_RECT);
+ marker_edit->connect(SceneStringName(draw), callable_mp(this, &AnimationTrackEditor::_redraw_groups));
+ marker_edit->connect(SceneStringName(draw), callable_mp(this, &AnimationTrackEditor::_redraw_tracks));
+
scroll = memnew(ScrollContainer);
box_selection_container->add_child(scroll);
scroll->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
@@ -7826,3 +7998,1203 @@ AnimationTrackKeyEditEditor::AnimationTrackKeyEditEditor(Ref<Animation> p_animat
AnimationTrackKeyEditEditor::~AnimationTrackKeyEditEditor() {
}
+
+void AnimationMarkerEdit::_zoom_changed() {
+ queue_redraw();
+ play_position->queue_redraw();
+}
+
+void AnimationMarkerEdit::_menu_selected(int p_index) {
+ switch (p_index) {
+ case MENU_KEY_INSERT: {
+ _insert_marker(insert_at_pos);
+ } break;
+ case MENU_KEY_RENAME: {
+ if (selection.size() > 0) {
+ _rename_marker(*selection.last());
+ }
+ } break;
+ case MENU_KEY_DELETE: {
+ _delete_selected_markers();
+ } break;
+ case MENU_KEY_TOGGLE_MARKER_NAMES: {
+ should_show_all_marker_names = !should_show_all_marker_names;
+ queue_redraw();
+ } break;
+ }
+}
+
+void AnimationMarkerEdit::_play_position_draw() {
+ if (animation.is_null() || play_position_pos < 0) {
+ return;
+ }
+
+ float scale = timeline->get_zoom_scale();
+ int h = get_size().height;
+
+ int px = (play_position_pos - timeline->get_value()) * scale + timeline->get_name_limit();
+
+ if (px >= timeline->get_name_limit() && px < (get_size().width - timeline->get_buttons_width())) {
+ Color color = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));
+ play_position->draw_line(Point2(px, 0), Point2(px, h), color, Math::round(2 * EDSCALE));
+ }
+}
+
+bool AnimationMarkerEdit::_try_select_at_ui_pos(const Point2 &p_pos, bool p_aggregate, bool p_deselectable) {
+ int limit = timeline->get_name_limit();
+ int limit_end = get_size().width - timeline->get_buttons_width();
+ // Left Border including space occupied by keyframes on t=0.
+ int limit_start_hitbox = limit - type_icon->get_width();
+
+ if (p_pos.x >= limit_start_hitbox && p_pos.x <= limit_end) {
+ int key_idx = -1;
+ float key_distance = 1e20;
+ PackedStringArray names = animation->get_marker_names();
+ for (int i = 0; i < names.size(); i++) {
+ Rect2 rect = const_cast<AnimationMarkerEdit *>(this)->get_key_rect(timeline->get_zoom_scale());
+ float offset = animation->get_marker_time(names[i]) - timeline->get_value();
+ offset = offset * timeline->get_zoom_scale() + limit;
+ rect.position.x += offset;
+ if (rect.has_point(p_pos)) {
+ if (const_cast<AnimationMarkerEdit *>(this)->is_key_selectable_by_distance()) {
+ float distance = Math::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) {
+ if (p_aggregate) {
+ StringName name = names[key_idx];
+ if (selection.has(name)) {
+ if (p_deselectable) {
+ call_deferred("_deselect_key", name);
+ moving_selection_pivot = 0.0f;
+ moving_selection_mouse_begin_x = 0.0f;
+ }
+ } else {
+ call_deferred("_select_key", name, false);
+ moving_selection_attempt = true;
+ moving_selection_effective = false;
+ select_single_attempt = StringName();
+ moving_selection_pivot = animation->get_marker_time(name);
+ moving_selection_mouse_begin_x = p_pos.x;
+ }
+
+ } else {
+ StringName name = names[key_idx];
+ if (!selection.has(name)) {
+ call_deferred("_select_key", name, true);
+ select_single_attempt = StringName();
+ } else {
+ select_single_attempt = name;
+ }
+
+ moving_selection_attempt = true;
+ moving_selection_effective = false;
+ moving_selection_pivot = animation->get_marker_time(name);
+ moving_selection_mouse_begin_x = p_pos.x;
+ }
+
+ if (read_only) {
+ moving_selection_attempt = false;
+ moving_selection_pivot = 0.0f;
+ moving_selection_mouse_begin_x = 0.0f;
+ }
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool AnimationMarkerEdit::_is_ui_pos_in_current_section(const Point2 &p_pos) {
+ int limit = timeline->get_name_limit();
+ int limit_end = get_size().width - timeline->get_buttons_width();
+
+ if (p_pos.x >= limit && p_pos.x <= limit_end) {
+ PackedStringArray section = get_selected_section();
+ if (!section.is_empty()) {
+ StringName start_marker = section[0];
+ StringName end_marker = section[1];
+ float start_offset = (animation->get_marker_time(start_marker) - timeline->get_value()) * timeline->get_zoom_scale() + limit;
+ float end_offset = (animation->get_marker_time(end_marker) - timeline->get_value()) * timeline->get_zoom_scale() + limit;
+ return p_pos.x >= start_offset && p_pos.x <= end_offset;
+ }
+ }
+
+ return false;
+}
+
+HBoxContainer *AnimationMarkerEdit::_create_hbox_labeled_control(const String &p_text, Control *p_control) const {
+ HBoxContainer *hbox = memnew(HBoxContainer);
+ Label *label = memnew(Label);
+ label->set_text(p_text);
+ hbox->add_child(label);
+ hbox->add_child(p_control);
+ hbox->set_h_size_flags(SIZE_EXPAND_FILL);
+ label->set_h_size_flags(SIZE_EXPAND_FILL);
+ label->set_stretch_ratio(1.0);
+ p_control->set_h_size_flags(SIZE_EXPAND_FILL);
+ p_control->set_stretch_ratio(1.0);
+ return hbox;
+}
+
+void AnimationMarkerEdit::_update_key_edit() {
+ _clear_key_edit();
+ if (animation.is_null()) {
+ return;
+ }
+
+ if (selection.size() == 1) {
+ key_edit = memnew(AnimationMarkerKeyEdit);
+ key_edit->animation = animation;
+ key_edit->animation_read_only = read_only;
+ key_edit->marker_name = *selection.begin();
+ key_edit->use_fps = timeline->is_using_fps();
+ key_edit->marker_edit = this;
+
+ EditorNode::get_singleton()->push_item(key_edit);
+
+ InspectorDock::get_singleton()->set_info(TTR("Marker name is read-only in the inspector."), TTR("A marker's name can only be changed by right-clicking it in the animation editor and selecting \"Rename Marker\", in order to make sure that marker names are all unique."), true);
+ } else if (selection.size() > 1) {
+ multi_key_edit = memnew(AnimationMultiMarkerKeyEdit);
+ multi_key_edit->animation = animation;
+ multi_key_edit->animation_read_only = read_only;
+ multi_key_edit->marker_edit = this;
+ for (const StringName &name : selection) {
+ multi_key_edit->marker_names.push_back(name);
+ }
+
+ EditorNode::get_singleton()->push_item(multi_key_edit);
+ }
+}
+
+void AnimationMarkerEdit::_clear_key_edit() {
+ if (key_edit) {
+ // If key edit is the object being inspected, remove it first.
+ if (InspectorDock::get_inspector_singleton()->get_edited_object() == key_edit) {
+ EditorNode::get_singleton()->push_item(nullptr);
+ }
+
+ // Then actually delete it.
+ memdelete(key_edit);
+ key_edit = nullptr;
+ }
+
+ if (multi_key_edit) {
+ if (InspectorDock::get_inspector_singleton()->get_edited_object() == multi_key_edit) {
+ EditorNode::get_singleton()->push_item(nullptr);
+ }
+
+ memdelete(multi_key_edit);
+ multi_key_edit = nullptr;
+ }
+}
+
+void AnimationMarkerEdit::_bind_methods() {
+ ClassDB::bind_method("_clear_selection_for_anim", &AnimationMarkerEdit::_clear_selection_for_anim);
+ ClassDB::bind_method("_select_key", &AnimationMarkerEdit::_select_key);
+ ClassDB::bind_method("_deselect_key", &AnimationMarkerEdit::_deselect_key);
+}
+
+void AnimationMarkerEdit::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_THEME_CHANGED: {
+ if (animation.is_null()) {
+ return;
+ }
+
+ type_icon = get_editor_theme_icon(SNAME("Marker"));
+ selected_icon = get_editor_theme_icon(SNAME("MarkerSelected"));
+ } break;
+
+ case NOTIFICATION_DRAW: {
+ if (animation.is_null()) {
+ return;
+ }
+
+ int limit = timeline->get_name_limit();
+
+ Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));
+ Color color = get_theme_color(SceneStringName(font_color), SNAME("Label"));
+ int hsep = get_theme_constant(SNAME("h_separation"), SNAME("ItemList"));
+ Color linecolor = color;
+ linecolor.a = 0.2;
+
+ // SECTION PREVIEW //
+
+ {
+ float scale = timeline->get_zoom_scale();
+ int limit_end = get_size().width - timeline->get_buttons_width();
+
+ PackedStringArray section = get_selected_section();
+ if (section.size() == 2) {
+ StringName start_marker = section[0];
+ StringName end_marker = section[1];
+ double start_time = animation->get_marker_time(start_marker);
+ double end_time = animation->get_marker_time(end_marker);
+
+ // When AnimationPlayer is playing, don't move the preview rect, so it still indicates the playback section.
+ AnimationPlayer *player = AnimationPlayerEditor::get_singleton()->get_player();
+ if (moving_selection && !(player && player->is_playing())) {
+ start_time += moving_selection_offset;
+ end_time += moving_selection_offset;
+ }
+
+ if (start_time < animation->get_length() && end_time >= 0) {
+ float start_ofs = MAX(0, start_time) - timeline->get_value();
+ float end_ofs = MIN(animation->get_length(), end_time) - timeline->get_value();
+ start_ofs = start_ofs * scale + limit;
+ end_ofs = end_ofs * scale + limit;
+ start_ofs = MAX(start_ofs, limit);
+ end_ofs = MIN(end_ofs, limit_end);
+ Rect2 rect;
+ rect.set_position(Vector2(start_ofs, 0));
+ rect.set_size(Vector2(end_ofs - start_ofs, get_size().height));
+
+ draw_rect(rect, Color(1, 0.1, 0.1, 0.2));
+ }
+ }
+ }
+
+ // KEYFRAMES //
+
+ 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();
+
+ PackedStringArray names = animation->get_marker_names();
+ for (int i = 0; i < names.size(); i++) {
+ StringName name = names[i];
+ bool is_selected = selection.has(name);
+ float offset = animation->get_marker_time(name) - timeline->get_value();
+ if (is_selected && moving_selection) {
+ offset += moving_selection_offset;
+ }
+
+ offset = offset * scale + limit;
+
+ draw_key(name, scale, int(offset), is_selected, limit, limit_end);
+
+ const int font_size = 16;
+ Size2 string_size = font->get_string_size(name, HORIZONTAL_ALIGNMENT_LEFT, -1.0, font_size);
+ if (int(offset) <= limit_end && int(offset) >= limit && should_show_all_marker_names) {
+ float bottom = get_size().height + string_size.y - font->get_descent(font_size);
+ float extrusion = MAX(0, offset + string_size.x - limit_end); // How much the string would extrude outside limit_end if unadjusted.
+ Color marker_color = animation->get_marker_color(name);
+ draw_string(font, Point2(offset - extrusion, bottom), name, HORIZONTAL_ALIGNMENT_LEFT, -1.0, font_size, marker_color);
+ draw_string_outline(font, Point2(offset - extrusion, bottom), name, HORIZONTAL_ALIGNMENT_LEFT, -1.0, font_size, 1, color);
+ }
+ }
+ }
+
+ draw_fg(limit, get_size().width - timeline->get_buttons_width());
+
+ // BUTTONS //
+
+ {
+ int ofs = get_size().width - timeline->get_buttons_width();
+
+ draw_line(Point2(ofs, 0), Point2(ofs, get_size().height), linecolor, Math::round(EDSCALE));
+
+ ofs += hsep;
+ }
+
+ draw_line(Vector2(0, get_size().height), get_size(), linecolor, Math::round(EDSCALE));
+ } break;
+
+ case NOTIFICATION_MOUSE_ENTER:
+ hovered = true;
+ queue_redraw();
+ break;
+ case NOTIFICATION_MOUSE_EXIT:
+ hovered = false;
+ // When the mouse cursor exits the track, we're no longer hovering any keyframe.
+ hovering_marker = StringName();
+ queue_redraw();
+ break;
+ }
+}
+
+void AnimationMarkerEdit::gui_input(const Ref<InputEvent> &p_event) {
+ ERR_FAIL_COND(p_event.is_null());
+
+ if (animation.is_null()) {
+ return;
+ }
+
+ if (p_event->is_pressed()) {
+ if (ED_IS_SHORTCUT("animation_marker_edit/rename_marker", p_event)) {
+ if (!read_only) {
+ _menu_selected(MENU_KEY_RENAME);
+ }
+ }
+
+ if (ED_IS_SHORTCUT("animation_marker_edit/delete_selection", p_event)) {
+ if (!read_only) {
+ _menu_selected(MENU_KEY_DELETE);
+ }
+ }
+
+ if (ED_IS_SHORTCUT("animation_marker_edit/toggle_marker_names", p_event)) {
+ if (!read_only) {
+ _menu_selected(MENU_KEY_TOGGLE_MARKER_NAMES);
+ }
+ }
+ }
+
+ Ref<InputEventMouseButton> mb = p_event;
+
+ if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
+ Point2 pos = mb->get_position();
+ if (_try_select_at_ui_pos(pos, mb->is_command_or_control_pressed() || mb->is_shift_pressed(), true)) {
+ accept_event();
+ } else if (!_is_ui_pos_in_current_section(pos)) {
+ _clear_selection_for_anim(animation);
+ }
+ }
+
+ if (mb.is_valid() && moving_selection_attempt) {
+ if (!mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
+ moving_selection_attempt = false;
+ if (moving_selection && moving_selection_effective) {
+ if (Math::abs(moving_selection_offset) > CMP_EPSILON) {
+ _move_selection_commit();
+ accept_event(); // So play position doesn't snap to the end of move selection.
+ }
+ } else if (select_single_attempt) {
+ call_deferred("_select_key", select_single_attempt, true);
+
+ // First select click should not affect play position.
+ if (!selection.has(select_single_attempt)) {
+ accept_event();
+ } else {
+ // Second click and onwards should snap to marker time.
+ double ofs = animation->get_marker_time(select_single_attempt);
+ timeline->set_play_position(ofs);
+ timeline->emit_signal(SNAME("timeline_changed"), ofs, mb->is_alt_pressed());
+ accept_event();
+ }
+ } else {
+ // First select click should not affect play position.
+ if (!selection.has(select_single_attempt)) {
+ accept_event();
+ }
+ }
+
+ moving_selection = false;
+ select_single_attempt = StringName();
+ }
+
+ if (moving_selection && mb->is_pressed() && mb->get_button_index() == MouseButton::RIGHT) {
+ moving_selection_attempt = false;
+ moving_selection = false;
+ _move_selection_cancel();
+ }
+ }
+
+ if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::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 (!read_only) {
+ bool selected = _try_select_at_ui_pos(pos, mb->is_command_or_control_pressed() || mb->is_shift_pressed(), false);
+
+ menu->clear();
+ menu->add_icon_item(get_editor_theme_icon(SNAME("Key")), TTR("Insert Marker..."), MENU_KEY_INSERT);
+
+ if (selected || selection.size() > 0) {
+ menu->add_icon_item(get_editor_theme_icon(SNAME("Edit")), TTR("Rename Marker"), MENU_KEY_RENAME);
+ menu->add_icon_item(get_editor_theme_icon(SNAME("Remove")), TTR("Delete Marker(s)"), MENU_KEY_DELETE);
+ }
+
+ menu->add_icon_item(get_editor_theme_icon(should_show_all_marker_names ? SNAME("GuiChecked") : SNAME("GuiUnchecked")), TTR("Show All Marker Names"), MENU_KEY_TOGGLE_MARKER_NAMES);
+ menu->reset_size();
+
+ moving_selection_attempt = false;
+ moving_selection = false;
+
+ menu->set_position(get_screen_position() + get_local_mouse_position());
+ menu->popup();
+
+ insert_at_pos = offset + timeline->get_value();
+ accept_event();
+ }
+ }
+ }
+
+ Ref<InputEventMouseMotion> mm = p_event;
+
+ if (mm.is_valid()) {
+ const StringName previous_hovering_marker = hovering_marker;
+
+ // Hovering compressed keyframes for editing is not possible.
+ const float scale = timeline->get_zoom_scale();
+ const int limit = timeline->get_name_limit();
+ const int limit_end = get_size().width - timeline->get_buttons_width();
+ // Left Border including space occupied by keyframes on t=0.
+ const int limit_start_hitbox = limit - type_icon->get_width();
+ const Point2 pos = mm->get_position();
+
+ if (pos.x >= limit_start_hitbox && pos.x <= limit_end) {
+ // Use the same logic as key selection to ensure that hovering accurately represents
+ // which key will be selected when clicking.
+ int key_idx = -1;
+ float key_distance = 1e20;
+
+ hovering_marker = StringName();
+
+ PackedStringArray names = animation->get_marker_names();
+
+ // Hovering should happen in the opposite order of drawing for more accurate overlap hovering.
+ for (int i = names.size() - 1; i >= 0; i--) {
+ StringName name = names[i];
+ Rect2 rect = get_key_rect(scale);
+ float offset = animation->get_marker_time(name) - timeline->get_value();
+ offset = offset * scale + limit;
+ rect.position.x += offset;
+
+ if (rect.has_point(pos)) {
+ if (is_key_selectable_by_distance()) {
+ const float distance = Math::abs(offset - pos.x);
+ if (key_idx == -1 || distance < key_distance) {
+ key_idx = i;
+ key_distance = distance;
+ hovering_marker = name;
+ }
+ } else {
+ // First one does it.
+ hovering_marker = name;
+ break;
+ }
+ }
+ }
+
+ if (hovering_marker != previous_hovering_marker) {
+ // Required to draw keyframe hover feedback on the correct keyframe.
+ queue_redraw();
+ }
+ }
+ }
+
+ if (mm.is_valid() && mm->get_button_mask().has_flag(MouseButtonMask::LEFT) && moving_selection_attempt) {
+ if (!moving_selection) {
+ moving_selection = true;
+ _move_selection_begin();
+ }
+
+ float moving_begin_time = ((moving_selection_mouse_begin_x - timeline->get_name_limit()) / timeline->get_zoom_scale()) + timeline->get_value();
+ float new_time = ((mm->get_position().x - timeline->get_name_limit()) / timeline->get_zoom_scale()) + timeline->get_value();
+ float delta = new_time - moving_begin_time;
+ float snapped_time = editor->snap_time(moving_selection_pivot + delta);
+
+ float offset = 0.0;
+ if (Math::abs(editor->get_moving_selection_offset()) > CMP_EPSILON || (snapped_time > moving_selection_pivot && delta > CMP_EPSILON) || (snapped_time < moving_selection_pivot && delta < -CMP_EPSILON)) {
+ offset = snapped_time - moving_selection_pivot;
+ moving_selection_effective = true;
+ }
+
+ _move_selection(offset);
+ }
+}
+
+String AnimationMarkerEdit::get_tooltip(const Point2 &p_pos) const {
+ if (animation.is_null()) {
+ return Control::get_tooltip(p_pos);
+ }
+
+ int limit = timeline->get_name_limit();
+ int limit_end = get_size().width - timeline->get_buttons_width();
+ // Left Border including space occupied by keyframes on t=0.
+ int limit_start_hitbox = limit - type_icon->get_width();
+
+ if (p_pos.x >= limit_start_hitbox && p_pos.x <= limit_end) {
+ int key_idx = -1;
+ float key_distance = 1e20;
+
+ PackedStringArray names = animation->get_marker_names();
+
+ // Select should happen in the opposite order of drawing for more accurate overlap select.
+ for (int i = names.size() - 1; i >= 0; i--) {
+ StringName name = names[i];
+ Rect2 rect = const_cast<AnimationMarkerEdit *>(this)->get_key_rect(timeline->get_zoom_scale());
+ float offset = animation->get_marker_time(name) - timeline->get_value();
+ offset = offset * timeline->get_zoom_scale() + limit;
+ rect.position.x += offset;
+
+ if (rect.has_point(p_pos)) {
+ if (const_cast<AnimationMarkerEdit *>(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 name = names[key_idx];
+ String text = TTR("Time (s):") + " " + TS->format_number(rtos(Math::snapped(animation->get_marker_time(name), 0.0001))) + "\n";
+ text += TTR("Marker:") + " " + name + "\n";
+ return text;
+ }
+ }
+
+ return Control::get_tooltip(p_pos);
+}
+
+int AnimationMarkerEdit::get_key_height() const {
+ if (animation.is_null()) {
+ return 0;
+ }
+
+ return type_icon->get_height();
+}
+
+Rect2 AnimationMarkerEdit::get_key_rect(float p_pixels_sec) const {
+ if (animation.is_null()) {
+ return Rect2();
+ }
+
+ Rect2 rect = Rect2(-type_icon->get_width() / 2, get_size().height - type_icon->get_size().height, type_icon->get_width(), type_icon->get_size().height);
+
+ // Make it a big easier to click.
+ rect.position.x -= rect.size.x * 0.5;
+ rect.size.x *= 2;
+ return rect;
+}
+
+PackedStringArray AnimationMarkerEdit::get_selected_section() const {
+ if (selection.size() >= 2) {
+ PackedStringArray arr;
+ arr.push_back(""); // Marker with smallest time.
+ arr.push_back(""); // Marker with largest time.
+ double min_time = INFINITY;
+ double max_time = -INFINITY;
+ for (const StringName &marker_name : selection) {
+ double time = animation->get_marker_time(marker_name);
+ if (time < min_time) {
+ arr.set(0, marker_name);
+ min_time = time;
+ }
+ if (time > max_time) {
+ arr.set(1, marker_name);
+ max_time = time;
+ }
+ }
+ return arr;
+ }
+
+ return PackedStringArray();
+}
+
+bool AnimationMarkerEdit::is_marker_selected(const StringName &p_marker) const {
+ return selection.has(p_marker);
+}
+
+bool AnimationMarkerEdit::is_key_selectable_by_distance() const {
+ return true;
+}
+
+void AnimationMarkerEdit::draw_key(const StringName &p_name, float p_pixels_sec, int p_x, bool p_selected, int p_clip_left, int p_clip_right) {
+ if (animation.is_null()) {
+ return;
+ }
+
+ if (p_x < p_clip_left || p_x > p_clip_right) {
+ return;
+ }
+
+ Ref<Texture2D> icon_to_draw = p_selected ? selected_icon : type_icon;
+
+ Vector2 ofs(p_x - icon_to_draw->get_width() / 2, int(get_size().height - icon_to_draw->get_height()));
+
+ // Don't apply custom marker color when the key is selected.
+ Color marker_color = p_selected ? Color(1, 1, 1) : animation->get_marker_color(p_name);
+
+ // Use a different color for the currently hovered key.
+ // The color multiplier is chosen to work with both dark and light editor themes,
+ // and on both unselected and selected key icons.
+ draw_texture(
+ icon_to_draw,
+ ofs,
+ p_name == hovering_marker ? get_theme_color(SNAME("folder_icon_color"), SNAME("FileDialog")) : marker_color);
+}
+
+void AnimationMarkerEdit::draw_bg(int p_clip_left, int p_clip_right) {
+}
+
+void AnimationMarkerEdit::draw_fg(int p_clip_left, int p_clip_right) {
+}
+
+Ref<Animation> AnimationMarkerEdit::get_animation() const {
+ return animation;
+}
+
+void AnimationMarkerEdit::set_animation(const Ref<Animation> &p_animation, bool p_read_only) {
+ if (animation.is_valid()) {
+ _clear_selection_for_anim(animation);
+ }
+ animation = p_animation;
+ read_only = p_read_only;
+ type_icon = get_editor_theme_icon(SNAME("Marker"));
+ selected_icon = get_editor_theme_icon(SNAME("MarkerSelected"));
+
+ queue_redraw();
+}
+
+Size2 AnimationMarkerEdit::get_minimum_size() const {
+ Ref<Texture2D> texture = get_editor_theme_icon(SNAME("Object"));
+ Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));
+ int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));
+ int separation = get_theme_constant(SNAME("v_separation"), SNAME("ItemList"));
+
+ int max_h = MAX(texture->get_height(), font->get_height(font_size));
+ max_h = MAX(max_h, get_key_height());
+
+ return Vector2(1, max_h + separation);
+}
+
+void AnimationMarkerEdit::set_timeline(AnimationTimelineEdit *p_timeline) {
+ timeline = p_timeline;
+ timeline->connect("zoom_changed", callable_mp(this, &AnimationMarkerEdit::_zoom_changed));
+ timeline->connect("name_limit_changed", callable_mp(this, &AnimationMarkerEdit::_zoom_changed));
+}
+
+void AnimationMarkerEdit::set_editor(AnimationTrackEditor *p_editor) {
+ editor = p_editor;
+}
+
+void AnimationMarkerEdit::set_play_position(float p_pos) {
+ play_position_pos = p_pos;
+ play_position->queue_redraw();
+}
+
+void AnimationMarkerEdit::update_play_position() {
+ play_position->queue_redraw();
+}
+
+void AnimationMarkerEdit::set_use_fps(bool p_use_fps) {
+ if (key_edit) {
+ key_edit->use_fps = p_use_fps;
+ key_edit->notify_property_list_changed();
+ }
+}
+
+void AnimationMarkerEdit::_move_selection_begin() {
+ moving_selection = true;
+ moving_selection_offset = 0;
+}
+
+void AnimationMarkerEdit::_move_selection(float p_offset) {
+ moving_selection_offset = p_offset;
+ queue_redraw();
+}
+
+void AnimationMarkerEdit::_move_selection_commit() {
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
+ undo_redo->create_action(TTR("Animation Move Markers"));
+
+ for (HashSet<StringName>::Iterator E = selection.last(); E; --E) {
+ StringName name = *E;
+ double time = animation->get_marker_time(name);
+ float newpos = time + moving_selection_offset;
+ undo_redo->add_do_method(animation.ptr(), "remove_marker", name);
+ undo_redo->add_do_method(animation.ptr(), "add_marker", name, newpos);
+ undo_redo->add_do_method(animation.ptr(), "set_marker_color", name, animation->get_marker_color(name));
+ undo_redo->add_undo_method(animation.ptr(), "remove_marker", name);
+ undo_redo->add_undo_method(animation.ptr(), "add_marker", name, time);
+ undo_redo->add_undo_method(animation.ptr(), "set_marker_color", name, animation->get_marker_color(name));
+
+ // add_marker will overwrite the overlapped key on the redo pass, so we add it back on the undo pass.
+ if (StringName overlap = animation->get_marker_at_time(newpos)) {
+ if (select_single_attempt == overlap) {
+ select_single_attempt = "";
+ }
+ undo_redo->add_undo_method(animation.ptr(), "add_marker", overlap, newpos);
+ undo_redo->add_undo_method(animation.ptr(), "set_marker_color", overlap, animation->get_marker_color(overlap));
+ }
+ }
+
+ moving_selection = false;
+ AnimationPlayer *player = AnimationPlayerEditor::get_singleton()->get_player();
+ if (player) {
+ PackedStringArray selected_section = get_selected_section();
+ if (selected_section.size() >= 2) {
+ undo_redo->add_do_method(player, "set_section_with_markers", selected_section[0], selected_section[1]);
+ undo_redo->add_undo_method(player, "set_section_with_markers", selected_section[0], selected_section[1]);
+ }
+ }
+ undo_redo->add_do_method(timeline, "queue_redraw");
+ undo_redo->add_undo_method(timeline, "queue_redraw");
+ undo_redo->add_do_method(this, "queue_redraw");
+ undo_redo->add_undo_method(this, "queue_redraw");
+ undo_redo->commit_action();
+ _update_key_edit();
+}
+
+void AnimationMarkerEdit::_delete_selected_markers() {
+ if (selection.size()) {
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
+ undo_redo->create_action(TTR("Animation Delete Keys"));
+ for (const StringName &name : selection) {
+ double time = animation->get_marker_time(name);
+ undo_redo->add_do_method(animation.ptr(), "remove_marker", name);
+ undo_redo->add_undo_method(animation.ptr(), "add_marker", name, time);
+ undo_redo->add_undo_method(animation.ptr(), "set_marker_color", name, animation->get_marker_color(name));
+ }
+ _clear_selection_for_anim(animation);
+
+ undo_redo->add_do_method(this, "queue_redraw");
+ undo_redo->add_undo_method(this, "queue_redraw");
+ undo_redo->commit_action();
+ _update_key_edit();
+ }
+}
+
+void AnimationMarkerEdit::_move_selection_cancel() {
+ moving_selection = false;
+ queue_redraw();
+}
+
+void AnimationMarkerEdit::_clear_selection(bool p_update) {
+ AnimationPlayer *player = AnimationPlayerEditor::get_singleton()->get_player();
+ if (player) {
+ player->reset_section();
+ }
+
+ selection.clear();
+
+ if (p_update) {
+ queue_redraw();
+ }
+
+ _clear_key_edit();
+}
+
+void AnimationMarkerEdit::_clear_selection_for_anim(const Ref<Animation> &p_anim) {
+ if (animation != p_anim) {
+ return;
+ }
+
+ _clear_selection(true);
+}
+
+void AnimationMarkerEdit::_select_key(const StringName &p_name, bool is_single) {
+ if (is_single) {
+ _clear_selection(false);
+ }
+
+ selection.insert(p_name);
+
+ AnimationPlayer *player = AnimationPlayerEditor::get_singleton()->get_player();
+ if (player) {
+ if (selection.size() >= 2) {
+ PackedStringArray selected_section = get_selected_section();
+ double start_time = animation->get_marker_time(selected_section[0]);
+ double end_time = animation->get_marker_time(selected_section[1]);
+ player->set_section(start_time, end_time);
+ } else {
+ player->reset_section();
+ }
+ }
+
+ queue_redraw();
+ _update_key_edit();
+
+ editor->_clear_selection(editor->is_selection_active());
+}
+
+void AnimationMarkerEdit::_deselect_key(const StringName &p_name) {
+ selection.erase(p_name);
+
+ AnimationPlayer *player = AnimationPlayerEditor::get_singleton()->get_player();
+ if (player) {
+ if (selection.size() >= 2) {
+ PackedStringArray selected_section = get_selected_section();
+ double start_time = animation->get_marker_time(selected_section[0]);
+ double end_time = animation->get_marker_time(selected_section[1]);
+ player->set_section(start_time, end_time);
+ } else {
+ player->reset_section();
+ }
+ }
+
+ queue_redraw();
+ _update_key_edit();
+}
+
+void AnimationMarkerEdit::_insert_marker(float p_ofs) {
+ if (editor->is_snap_timeline_enabled()) {
+ p_ofs = editor->snap_time(p_ofs);
+ }
+
+ marker_insert_confirm->popup_centered(Size2(200, 100) * EDSCALE);
+ marker_insert_color->set_pick_color(Color(1, 1, 1));
+
+ String base = "new_marker";
+ int count = 1;
+ while (true) {
+ String attempt = base;
+ if (count > 1) {
+ attempt += vformat("_%d", count);
+ }
+ if (animation->has_marker(attempt)) {
+ count++;
+ continue;
+ }
+ base = attempt;
+ break;
+ }
+
+ marker_insert_new_name->set_text(base);
+ _marker_insert_new_name_changed(base);
+ marker_insert_ofs = p_ofs;
+}
+
+void AnimationMarkerEdit::_rename_marker(const StringName &p_name) {
+ marker_rename_confirm->popup_centered(Size2i(200, 0) * EDSCALE);
+ marker_rename_prev_name = p_name;
+ marker_rename_new_name->set_text(p_name);
+}
+
+void AnimationMarkerEdit::_marker_insert_confirmed() {
+ StringName name = marker_insert_new_name->get_text();
+
+ if (animation->has_marker(name)) {
+ marker_insert_error_dialog->set_text(vformat(TTR("Marker '%s' already exists!"), name));
+ marker_insert_error_dialog->popup_centered();
+ return;
+ }
+
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
+
+ undo_redo->create_action(TTR("Add Marker Key"));
+ undo_redo->add_do_method(animation.ptr(), "add_marker", name, marker_insert_ofs);
+ undo_redo->add_undo_method(animation.ptr(), "remove_marker", name);
+ StringName existing_marker = animation->get_marker_at_time(marker_insert_ofs);
+ if (existing_marker) {
+ undo_redo->add_undo_method(animation.ptr(), "add_marker", existing_marker, marker_insert_ofs);
+ undo_redo->add_undo_method(animation.ptr(), "set_marker_color", existing_marker, animation->get_marker_color(existing_marker));
+ }
+ undo_redo->add_do_method(animation.ptr(), "set_marker_color", name, marker_insert_color->get_pick_color());
+
+ undo_redo->add_do_method(this, "queue_redraw");
+ undo_redo->add_undo_method(this, "queue_redraw");
+
+ undo_redo->commit_action();
+
+ marker_insert_confirm->hide();
+}
+
+void AnimationMarkerEdit::_marker_insert_new_name_changed(const String &p_text) {
+ marker_insert_confirm->get_ok_button()->set_disabled(p_text.is_empty());
+}
+
+void AnimationMarkerEdit::_marker_rename_confirmed() {
+ StringName new_name = marker_rename_new_name->get_text();
+ StringName prev_name = marker_rename_prev_name;
+
+ if (new_name == StringName()) {
+ marker_rename_error_dialog->set_text(TTR("Empty marker names are not allowed."));
+ marker_rename_error_dialog->popup_centered();
+ return;
+ }
+
+ if (new_name != prev_name && animation->has_marker(new_name)) {
+ marker_rename_error_dialog->set_text(vformat(TTR("Marker '%s' already exists!"), new_name));
+ marker_rename_error_dialog->popup_centered();
+ return;
+ }
+
+ if (prev_name != new_name) {
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
+ undo_redo->create_action(TTR("Rename Marker"));
+ undo_redo->add_do_method(animation.ptr(), "remove_marker", prev_name);
+ undo_redo->add_do_method(animation.ptr(), "add_marker", new_name, animation->get_marker_time(prev_name));
+ undo_redo->add_do_method(animation.ptr(), "set_marker_color", new_name, animation->get_marker_color(prev_name));
+ undo_redo->add_undo_method(animation.ptr(), "remove_marker", new_name);
+ undo_redo->add_undo_method(animation.ptr(), "add_marker", prev_name, animation->get_marker_time(prev_name));
+ undo_redo->add_undo_method(animation.ptr(), "set_marker_color", prev_name, animation->get_marker_color(prev_name));
+ undo_redo->add_do_method(this, "_select_key", new_name, true);
+ undo_redo->add_undo_method(this, "_select_key", prev_name, true);
+ undo_redo->commit_action();
+ select_single_attempt = StringName();
+ }
+ marker_rename_confirm->hide();
+}
+
+void AnimationMarkerEdit::_marker_rename_new_name_changed(const String &p_text) {
+ marker_rename_confirm->get_ok_button()->set_disabled(p_text.is_empty());
+}
+
+AnimationMarkerEdit::AnimationMarkerEdit() {
+ play_position = memnew(Control);
+ play_position->set_mouse_filter(MOUSE_FILTER_PASS);
+ add_child(play_position);
+ play_position->connect(SceneStringName(draw), callable_mp(this, &AnimationMarkerEdit::_play_position_draw));
+ set_focus_mode(FOCUS_CLICK);
+ set_mouse_filter(MOUSE_FILTER_PASS); // Scroll has to work too for selection.
+
+ menu = memnew(PopupMenu);
+ add_child(menu);
+ menu->connect(SceneStringName(id_pressed), callable_mp(this, &AnimationMarkerEdit::_menu_selected));
+ menu->add_shortcut(ED_SHORTCUT("animation_marker_edit/rename_marker", TTR("Rename Marker"), Key::R), MENU_KEY_RENAME);
+ menu->add_shortcut(ED_SHORTCUT("animation_marker_edit/delete_selection", TTR("Delete Markers (s)"), Key::KEY_DELETE), MENU_KEY_DELETE);
+ menu->add_shortcut(ED_SHORTCUT("animation_marker_edit/toggle_marker_names", TTR("Show All Marker Names"), Key::M), MENU_KEY_TOGGLE_MARKER_NAMES);
+
+ marker_insert_confirm = memnew(ConfirmationDialog);
+ marker_insert_confirm->set_title(TTR("Insert Marker"));
+ marker_insert_confirm->set_hide_on_ok(false);
+ marker_insert_confirm->connect(SceneStringName(confirmed), callable_mp(this, &AnimationMarkerEdit::_marker_insert_confirmed));
+ add_child(marker_insert_confirm);
+ VBoxContainer *marker_insert_vbox = memnew(VBoxContainer);
+ marker_insert_vbox->set_anchors_and_offsets_preset(Control::LayoutPreset::PRESET_FULL_RECT);
+ marker_insert_confirm->add_child(marker_insert_vbox);
+ marker_insert_new_name = memnew(LineEdit);
+ marker_insert_new_name->connect(SceneStringName(text_changed), callable_mp(this, &AnimationMarkerEdit::_marker_insert_new_name_changed));
+ marker_insert_confirm->register_text_enter(marker_insert_new_name);
+ marker_insert_vbox->add_child(_create_hbox_labeled_control(TTR("Marker Name"), marker_insert_new_name));
+ marker_insert_color = memnew(ColorPickerButton);
+ marker_insert_color->set_edit_alpha(false);
+ marker_insert_color->get_popup()->connect("about_to_popup", callable_mp(EditorNode::get_singleton(), &EditorNode::setup_color_picker).bind(marker_insert_color->get_picker()));
+ marker_insert_vbox->add_child(_create_hbox_labeled_control(TTR("Marker Color"), marker_insert_color));
+ marker_insert_error_dialog = memnew(AcceptDialog);
+ marker_insert_error_dialog->set_ok_button_text(TTR("Close"));
+ marker_insert_error_dialog->set_title(TTR("Error!"));
+ marker_insert_confirm->add_child(marker_insert_error_dialog);
+
+ marker_rename_confirm = memnew(ConfirmationDialog);
+ marker_rename_confirm->set_title(TTR("Rename Marker"));
+ marker_rename_confirm->set_hide_on_ok(false);
+ marker_rename_confirm->connect(SceneStringName(confirmed), callable_mp(this, &AnimationMarkerEdit::_marker_rename_confirmed));
+ add_child(marker_rename_confirm);
+ VBoxContainer *marker_rename_vbox = memnew(VBoxContainer);
+ marker_rename_vbox->set_anchors_and_offsets_preset(Control::LayoutPreset::PRESET_FULL_RECT);
+ marker_rename_confirm->add_child(marker_rename_vbox);
+ Label *marker_rename_new_name_label = memnew(Label);
+ marker_rename_new_name_label->set_text(TTR("Change Marker Name:"));
+ marker_rename_vbox->add_child(marker_rename_new_name_label);
+ marker_rename_new_name = memnew(LineEdit);
+ marker_rename_new_name->connect(SceneStringName(text_changed), callable_mp(this, &AnimationMarkerEdit::_marker_rename_new_name_changed));
+ marker_rename_confirm->register_text_enter(marker_rename_new_name);
+ marker_rename_vbox->add_child(marker_rename_new_name);
+
+ marker_rename_error_dialog = memnew(AcceptDialog);
+ marker_rename_error_dialog->set_ok_button_text(TTR("Close"));
+ marker_rename_error_dialog->set_title(TTR("Error!"));
+ marker_rename_confirm->add_child(marker_rename_error_dialog);
+}
+
+AnimationMarkerEdit::~AnimationMarkerEdit() {
+}
+
+float AnimationMarkerKeyEdit::get_time() const {
+ return animation->get_marker_time(marker_name);
+}
+
+void AnimationMarkerKeyEdit::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("_hide_script_from_inspector"), &AnimationMarkerKeyEdit::_hide_script_from_inspector);
+ ClassDB::bind_method(D_METHOD("_hide_metadata_from_inspector"), &AnimationMarkerKeyEdit::_hide_metadata_from_inspector);
+ ClassDB::bind_method(D_METHOD("_dont_undo_redo"), &AnimationMarkerKeyEdit::_dont_undo_redo);
+ ClassDB::bind_method(D_METHOD("_is_read_only"), &AnimationMarkerKeyEdit::_is_read_only);
+ ClassDB::bind_method(D_METHOD("_set_marker_name"), &AnimationMarkerKeyEdit::_set_marker_name);
+}
+
+void AnimationMarkerKeyEdit::_set_marker_name(const StringName &p_name) {
+ marker_name = p_name;
+}
+
+bool AnimationMarkerKeyEdit::_set(const StringName &p_name, const Variant &p_value) {
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
+
+ if (p_name == "color") {
+ Color color = p_value;
+ Color prev_color = animation->get_marker_color(marker_name);
+ if (color != prev_color) {
+ undo_redo->create_action(TTR("Edit Marker Color"), UndoRedo::MERGE_ENDS);
+ undo_redo->add_do_method(animation.ptr(), "set_marker_color", marker_name, color);
+ undo_redo->add_undo_method(animation.ptr(), "set_marker_color", marker_name, prev_color);
+ undo_redo->add_do_method(marker_edit, "queue_redraw");
+ undo_redo->add_undo_method(marker_edit, "queue_redraw");
+ undo_redo->commit_action();
+ }
+ return true;
+ }
+
+ return false;
+}
+
+bool AnimationMarkerKeyEdit::_get(const StringName &p_name, Variant &r_ret) const {
+ if (p_name == "name") {
+ r_ret = marker_name;
+ return true;
+ }
+
+ if (p_name == "color") {
+ r_ret = animation->get_marker_color(marker_name);
+ return true;
+ }
+
+ return false;
+}
+
+void AnimationMarkerKeyEdit::_get_property_list(List<PropertyInfo> *p_list) const {
+ if (animation.is_null()) {
+ return;
+ }
+
+ p_list->push_back(PropertyInfo(Variant::STRING_NAME, "name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_READ_ONLY | PROPERTY_USAGE_EDITOR));
+ p_list->push_back(PropertyInfo(Variant::COLOR, "color", PROPERTY_HINT_COLOR_NO_ALPHA));
+}
+
+void AnimationMultiMarkerKeyEdit::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("_hide_script_from_inspector"), &AnimationMultiMarkerKeyEdit::_hide_script_from_inspector);
+ ClassDB::bind_method(D_METHOD("_hide_metadata_from_inspector"), &AnimationMultiMarkerKeyEdit::_hide_metadata_from_inspector);
+ ClassDB::bind_method(D_METHOD("_dont_undo_redo"), &AnimationMultiMarkerKeyEdit::_dont_undo_redo);
+ ClassDB::bind_method(D_METHOD("_is_read_only"), &AnimationMultiMarkerKeyEdit::_is_read_only);
+}
+
+bool AnimationMultiMarkerKeyEdit::_set(const StringName &p_name, const Variant &p_value) {
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
+ if (p_name == "color") {
+ Color color = p_value;
+
+ undo_redo->create_action(TTR("Multi Edit Marker Color"), UndoRedo::MERGE_ENDS);
+
+ for (const StringName &marker_name : marker_names) {
+ undo_redo->add_do_method(animation.ptr(), "set_marker_color", marker_name, color);
+ undo_redo->add_undo_method(animation.ptr(), "set_marker_color", marker_name, animation->get_marker_color(marker_name));
+ }
+
+ undo_redo->add_do_method(marker_edit, "queue_redraw");
+ undo_redo->add_undo_method(marker_edit, "queue_redraw");
+ undo_redo->commit_action();
+
+ return true;
+ }
+
+ return false;
+}
+
+bool AnimationMultiMarkerKeyEdit::_get(const StringName &p_name, Variant &r_ret) const {
+ if (p_name == "color") {
+ r_ret = animation->get_marker_color(marker_names[0]);
+ return true;
+ }
+
+ return false;
+}
+
+void AnimationMultiMarkerKeyEdit::_get_property_list(List<PropertyInfo> *p_list) const {
+ if (animation.is_null()) {
+ return;
+ }
+
+ p_list->push_back(PropertyInfo(Variant::COLOR, "color", PROPERTY_HINT_COLOR_NO_ALPHA));
+}
+
+// AnimationMarkerKeyEditEditorPlugin
+
+void AnimationMarkerKeyEditEditor::_time_edit_entered() {
+}
+
+void AnimationMarkerKeyEditEditor::_time_edit_exited() {
+ real_t new_time = spinner->get_value();
+
+ if (use_fps) {
+ real_t fps = animation->get_step();
+ if (fps > 0) {
+ fps = 1.0 / fps;
+ }
+ new_time /= fps;
+ }
+
+ real_t prev_time = animation->get_marker_time(marker_name);
+
+ if (Math::is_equal_approx(new_time, prev_time)) {
+ return; // No change.
+ }
+
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
+ undo_redo->create_action(TTR("Animation Change Marker Time"), UndoRedo::MERGE_ENDS);
+
+ Color color = animation->get_marker_color(marker_name);
+ undo_redo->add_do_method(animation.ptr(), "add_marker", marker_name, new_time);
+ undo_redo->add_do_method(animation.ptr(), "set_marker_color", marker_name, color);
+ undo_redo->add_undo_method(animation.ptr(), "remove_marker", marker_name);
+ undo_redo->add_undo_method(animation.ptr(), "add_marker", marker_name, prev_time);
+ undo_redo->add_undo_method(animation.ptr(), "set_marker_color", marker_name, color);
+ StringName existing_marker = animation->get_marker_at_time(new_time);
+ if (existing_marker) {
+ undo_redo->add_undo_method(animation.ptr(), "add_marker", existing_marker, animation->get_marker_time(existing_marker));
+ undo_redo->add_undo_method(animation.ptr(), "set_marker_color", existing_marker, animation->get_marker_color(existing_marker));
+ }
+ AnimationPlayerEditor *ape = AnimationPlayerEditor::get_singleton();
+ if (ape) {
+ AnimationTrackEditor *ate = ape->get_track_editor();
+ if (ate) {
+ AnimationMarkerEdit *ame = ate->marker_edit;
+ undo_redo->add_do_method(ame, "queue_redraw");
+ undo_redo->add_undo_method(ame, "queue_redraw");
+ }
+ }
+ undo_redo->commit_action();
+}
+
+AnimationMarkerKeyEditEditor::AnimationMarkerKeyEditEditor(Ref<Animation> p_animation, const StringName &p_name, bool p_use_fps) {
+ if (p_animation.is_null()) {
+ return;
+ }
+
+ animation = p_animation;
+ use_fps = p_use_fps;
+ marker_name = p_name;
+
+ set_label("Time");
+
+ spinner = memnew(EditorSpinSlider);
+ spinner->set_focus_mode(Control::FOCUS_CLICK);
+ spinner->set_min(0);
+ spinner->set_allow_greater(true);
+ spinner->set_allow_lesser(true);
+
+ float time = animation->get_marker_time(marker_name);
+
+ if (use_fps) {
+ spinner->set_step(FPS_DECIMAL);
+ real_t fps = animation->get_step();
+ if (fps > 0) {
+ fps = 1.0 / fps;
+ }
+ spinner->set_value(time * fps);
+ } else {
+ spinner->set_step(SECOND_DECIMAL);
+ spinner->set_value(time);
+ spinner->set_max(animation->get_length());
+ }
+
+ add_child(spinner);
+
+ spinner->connect("grabbed", callable_mp(this, &AnimationMarkerKeyEditEditor::_time_edit_entered), CONNECT_DEFERRED);
+ spinner->connect("ungrabbed", callable_mp(this, &AnimationMarkerKeyEditEditor::_time_edit_exited), CONNECT_DEFERRED);
+ spinner->connect("value_focus_entered", callable_mp(this, &AnimationMarkerKeyEditEditor::_time_edit_entered), CONNECT_DEFERRED);
+ spinner->connect("value_focus_exited", callable_mp(this, &AnimationMarkerKeyEditEditor::_time_edit_exited), CONNECT_DEFERRED);
+}
+
+AnimationMarkerKeyEditEditor::~AnimationMarkerKeyEditEditor() {
+}
diff --git a/editor/animation_track_editor.h b/editor/animation_track_editor.h
index 6b9140ddaa..0da474afd4 100644
--- a/editor/animation_track_editor.h
+++ b/editor/animation_track_editor.h
@@ -41,9 +41,11 @@
#include "scene/gui/tree.h"
#include "scene/resources/animation.h"
+class AnimationMarkerEdit;
class AnimationTrackEditor;
class AnimationTrackEdit;
class CheckBox;
+class ColorPickerButton;
class EditorSpinSlider;
class HSlider;
class OptionButton;
@@ -52,6 +54,7 @@ class SceneTreeDialog;
class SpinBox;
class TextureRect;
class ViewPanner;
+class EditorValidationPanel;
class AnimationTrackKeyEdit : public Object {
GDCLASS(AnimationTrackKeyEdit, Object);
@@ -128,6 +131,58 @@ protected:
void _get_property_list(List<PropertyInfo> *p_list) const;
};
+class AnimationMarkerKeyEdit : public Object {
+ GDCLASS(AnimationMarkerKeyEdit, Object);
+
+public:
+ bool animation_read_only = false;
+
+ Ref<Animation> animation;
+ StringName marker_name;
+ bool use_fps = false;
+
+ AnimationMarkerEdit *marker_edit = nullptr;
+
+ bool _hide_script_from_inspector() { return true; }
+ bool _hide_metadata_from_inspector() { return true; }
+ bool _dont_undo_redo() { return true; }
+
+ bool _is_read_only() { return animation_read_only; }
+
+ float get_time() const;
+
+protected:
+ static void _bind_methods();
+ void _set_marker_name(const StringName &p_name);
+ 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;
+};
+
+class AnimationMultiMarkerKeyEdit : public Object {
+ GDCLASS(AnimationMultiMarkerKeyEdit, Object);
+
+public:
+ bool animation_read_only = false;
+
+ Ref<Animation> animation;
+ Vector<StringName> marker_names;
+
+ AnimationMarkerEdit *marker_edit = nullptr;
+
+ bool _hide_script_from_inspector() { return true; }
+ bool _hide_metadata_from_inspector() { return true; }
+ bool _dont_undo_redo() { return true; }
+
+ bool _is_read_only() { return animation_read_only; }
+
+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;
+};
+
class AnimationTimelineEdit : public Range {
GDCLASS(AnimationTimelineEdit, Range);
@@ -218,6 +273,140 @@ public:
AnimationTimelineEdit();
};
+class AnimationMarkerEdit : public Control {
+ GDCLASS(AnimationMarkerEdit, Control);
+ friend class AnimationTimelineEdit;
+
+ enum {
+ MENU_KEY_INSERT,
+ MENU_KEY_RENAME,
+ MENU_KEY_DELETE,
+ MENU_KEY_TOGGLE_MARKER_NAMES,
+ };
+
+ AnimationTimelineEdit *timeline = nullptr;
+ Control *play_position = nullptr; // Separate control used to draw so updates for only position changed are much faster.
+ float play_position_pos = 0.0f;
+
+ HashSet<StringName> selection;
+
+ Ref<Animation> animation;
+ bool read_only = false;
+
+ Ref<Texture2D> type_icon;
+ Ref<Texture2D> selected_icon;
+
+ PopupMenu *menu = nullptr;
+
+ bool hovered = false;
+ StringName hovering_marker;
+
+ void _zoom_changed();
+
+ Ref<Texture2D> icon_cache;
+
+ void _menu_selected(int p_index);
+
+ void _play_position_draw();
+ bool _try_select_at_ui_pos(const Point2 &p_pos, bool p_aggregate, bool p_deselectable);
+ bool _is_ui_pos_in_current_section(const Point2 &p_pos);
+
+ float insert_at_pos = 0.0f;
+ bool moving_selection_attempt = false;
+ bool moving_selection_effective = false;
+ float moving_selection_offset = 0.0f;
+ float moving_selection_pivot = 0.0f;
+ float moving_selection_mouse_begin_x = 0.0f;
+ float moving_selection_mouse_begin_y = 0.0f;
+ StringName select_single_attempt;
+ bool moving_selection = false;
+ void _move_selection_begin();
+ void _move_selection(float p_offset);
+ void _move_selection_commit();
+ void _move_selection_cancel();
+
+ void _clear_selection_for_anim(const Ref<Animation> &p_anim);
+ void _select_key(const StringName &p_name, bool is_single = false);
+ void _deselect_key(const StringName &p_name);
+
+ void _insert_marker(float p_ofs);
+ void _rename_marker(const StringName &p_name);
+ void _delete_selected_markers();
+
+ ConfirmationDialog *marker_insert_confirm = nullptr;
+ LineEdit *marker_insert_new_name = nullptr;
+ ColorPickerButton *marker_insert_color = nullptr;
+ AcceptDialog *marker_insert_error_dialog = nullptr;
+ float marker_insert_ofs = 0;
+
+ ConfirmationDialog *marker_rename_confirm = nullptr;
+ LineEdit *marker_rename_new_name = nullptr;
+ StringName marker_rename_prev_name;
+
+ AcceptDialog *marker_rename_error_dialog = nullptr;
+
+ bool should_show_all_marker_names = false;
+
+ ////////////// edit menu stuff
+
+ void _marker_insert_confirmed();
+ void _marker_insert_new_name_changed(const String &p_text);
+ void _marker_rename_confirmed();
+ void _marker_rename_new_name_changed(const String &p_text);
+
+ AnimationTrackEditor *editor = nullptr;
+
+ HBoxContainer *_create_hbox_labeled_control(const String &p_text, Control *p_control) const;
+
+ void _update_key_edit();
+ void _clear_key_edit();
+
+ AnimationMarkerKeyEdit *key_edit = nullptr;
+ AnimationMultiMarkerKeyEdit *multi_key_edit = nullptr;
+
+protected:
+ static void _bind_methods();
+ void _notification(int p_what);
+
+ virtual void gui_input(const Ref<InputEvent> &p_event) override;
+
+public:
+ virtual String get_tooltip(const Point2 &p_pos) const override;
+
+ virtual int get_key_height() const;
+ virtual Rect2 get_key_rect(float p_pixels_sec) const;
+ virtual bool is_key_selectable_by_distance() const;
+ virtual void draw_key(const StringName &p_name, 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);
+
+ Ref<Animation> get_animation() const;
+ AnimationTimelineEdit *get_timeline() const { return timeline; }
+ AnimationTrackEditor *get_editor() const { return editor; }
+ bool is_selection_active() const { return !selection.is_empty(); }
+ bool is_moving_selection() const { return moving_selection; }
+ float get_moving_selection_offset() const { return moving_selection_offset; }
+ void set_animation(const Ref<Animation> &p_animation, bool p_read_only);
+ virtual Size2 get_minimum_size() const override;
+
+ void set_timeline(AnimationTimelineEdit *p_timeline);
+ void set_editor(AnimationTrackEditor *p_editor);
+
+ void set_play_position(float p_pos);
+ void update_play_position();
+
+ void set_use_fps(bool p_use_fps);
+
+ PackedStringArray get_selected_section() const;
+ bool is_marker_selected(const StringName &p_marker) const;
+
+ // For use by AnimationTrackEditor.
+ void _clear_selection(bool p_update);
+
+ AnimationMarkerEdit();
+ ~AnimationMarkerEdit();
+};
+
class AnimationTrackEdit : public Control {
GDCLASS(AnimationTrackEdit, Control);
friend class AnimationTimelineEdit;
@@ -367,6 +556,7 @@ class AnimationTrackEditGroup : public Control {
NodePath node;
Node *root = nullptr;
AnimationTimelineEdit *timeline = nullptr;
+ AnimationTrackEditor *editor = nullptr;
void _zoom_changed();
@@ -380,6 +570,7 @@ public:
virtual Size2 get_minimum_size() const override;
void set_timeline(AnimationTimelineEdit *p_timeline);
void set_root(Node *p_root);
+ void set_editor(AnimationTrackEditor *p_editor);
AnimationTrackEditGroup();
};
@@ -388,6 +579,7 @@ class AnimationTrackEditor : public VBoxContainer {
GDCLASS(AnimationTrackEditor, VBoxContainer);
friend class AnimationTimelineEdit;
friend class AnimationBezierTrackEdit;
+ friend class AnimationMarkerKeyEditEditor;
Ref<Animation> animation;
bool read_only = false;
@@ -405,6 +597,7 @@ class AnimationTrackEditor : public VBoxContainer {
Label *info_message = nullptr;
AnimationTimelineEdit *timeline = nullptr;
+ AnimationMarkerEdit *marker_edit = nullptr;
HSlider *zoom = nullptr;
EditorSpinSlider *step = nullptr;
TextureRect *zoom_icon = nullptr;
@@ -743,6 +936,10 @@ public:
float get_moving_selection_offset() const;
float snap_time(float p_value, bool p_relative = false);
bool is_grouping_tracks();
+ PackedStringArray get_selected_section() const;
+ bool is_marker_selected(const StringName &p_marker) const;
+ bool is_marker_moving_selection() const;
+ float get_marker_moving_selection_offset() const;
/** If `p_from_mouse_event` is `true`, handle Shift key presses for precise snapping. */
void goto_prev_step(bool p_from_mouse_event);
@@ -781,4 +978,23 @@ public:
~AnimationTrackKeyEditEditor();
};
+// AnimationMarkerKeyEditEditorPlugin
+
+class AnimationMarkerKeyEditEditor : public EditorProperty {
+ GDCLASS(AnimationMarkerKeyEditEditor, EditorProperty);
+
+ Ref<Animation> animation;
+ StringName marker_name;
+ bool use_fps = false;
+
+ EditorSpinSlider *spinner = nullptr;
+
+ void _time_edit_entered();
+ void _time_edit_exited();
+
+public:
+ AnimationMarkerKeyEditEditor(Ref<Animation> p_animation, const StringName &p_name, bool p_use_fps);
+ ~AnimationMarkerKeyEditEditor();
+};
+
#endif // ANIMATION_TRACK_EDITOR_H
diff --git a/editor/debugger/editor_debugger_server.cpp b/editor/debugger/editor_debugger_server.cpp
index c0efc6a1fc..9ec6058132 100644
--- a/editor/debugger/editor_debugger_server.cpp
+++ b/editor/debugger/editor_debugger_server.cpp
@@ -77,8 +77,8 @@ Error EditorDebuggerServerTCP::start(const String &p_uri) {
// Optionally override
if (!p_uri.is_empty() && p_uri != "tcp://") {
- String scheme, path;
- Error err = p_uri.parse_url(scheme, bind_host, bind_port, path);
+ String scheme, path, fragment;
+ Error err = p_uri.parse_url(scheme, bind_host, bind_port, path, fragment);
ERR_FAIL_COND_V(err != OK, ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V(!bind_host.is_valid_ip_address() && bind_host != "*", ERR_INVALID_PARAMETER);
}
diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp
index bc46d01892..afa0548044 100644
--- a/editor/editor_file_system.cpp
+++ b/editor/editor_file_system.cpp
@@ -1978,7 +1978,10 @@ void EditorFileSystem::_update_file_icon_path(EditorFileSystemDirectory::FileInf
}
if (icon_path.is_empty() && !file_info->type.is_empty()) {
- icon_path = EditorNode::get_singleton()->get_class_icon(file_info->type)->get_path();
+ Ref<Texture2D> icon = EditorNode::get_singleton()->get_class_icon(file_info->type);
+ if (icon.is_valid()) {
+ icon_path = icon->get_path();
+ }
}
file_info->icon_path = icon_path;
diff --git a/editor/editor_file_system.h b/editor/editor_file_system.h
index 255eaff10d..7aa0137f4e 100644
--- a/editor/editor_file_system.h
+++ b/editor/editor_file_system.h
@@ -114,9 +114,9 @@ class EditorFileSystemImportFormatSupportQuery : public RefCounted {
GDCLASS(EditorFileSystemImportFormatSupportQuery, RefCounted);
protected:
- GDVIRTUAL0RC(bool, _is_active)
- GDVIRTUAL0RC(Vector<String>, _get_file_extensions)
- GDVIRTUAL0RC(bool, _query)
+ GDVIRTUAL0RC_REQUIRED(bool, _is_active)
+ GDVIRTUAL0RC_REQUIRED(Vector<String>, _get_file_extensions)
+ GDVIRTUAL0RC_REQUIRED(bool, _query)
static void _bind_methods() {
GDVIRTUAL_BIND(_is_active);
GDVIRTUAL_BIND(_get_file_extensions);
@@ -126,17 +126,17 @@ protected:
public:
virtual bool is_active() const {
bool ret = false;
- GDVIRTUAL_REQUIRED_CALL(_is_active, ret);
+ GDVIRTUAL_CALL(_is_active, ret);
return ret;
}
virtual Vector<String> get_file_extensions() const {
Vector<String> ret;
- GDVIRTUAL_REQUIRED_CALL(_get_file_extensions, ret);
+ GDVIRTUAL_CALL(_get_file_extensions, ret);
return ret;
}
virtual bool query() {
bool ret = false;
- GDVIRTUAL_REQUIRED_CALL(_query, ret);
+ GDVIRTUAL_CALL(_query, ret);
return ret;
}
};
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 665255b9b2..3bae9ae984 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -7722,6 +7722,7 @@ EditorNode::EditorNode() {
add_editor_plugin(memnew(AnimationPlayerEditorPlugin));
add_editor_plugin(memnew(AnimationTrackKeyEditEditorPlugin));
+ add_editor_plugin(memnew(AnimationMarkerKeyEditEditorPlugin));
add_editor_plugin(memnew(CanvasItemEditorPlugin));
add_editor_plugin(memnew(Node3DEditorPlugin));
add_editor_plugin(memnew(ScriptEditorPlugin));
diff --git a/editor/editor_script.cpp b/editor/editor_script.cpp
index 30a4b6811c..6968c7f25a 100644
--- a/editor/editor_script.cpp
+++ b/editor/editor_script.cpp
@@ -78,7 +78,7 @@ EditorInterface *EditorScript::get_editor_interface() const {
}
void EditorScript::run() {
- GDVIRTUAL_REQUIRED_CALL(_run);
+ GDVIRTUAL_CALL(_run);
}
void EditorScript::_bind_methods() {
diff --git a/editor/editor_script.h b/editor/editor_script.h
index 72e7641df7..b0724c31c0 100644
--- a/editor/editor_script.h
+++ b/editor/editor_script.h
@@ -44,7 +44,7 @@ class EditorScript : public RefCounted {
protected:
static void _bind_methods();
- GDVIRTUAL0(_run)
+ GDVIRTUAL0_REQUIRED(_run)
public:
void add_root_node(Node *p_node);
diff --git a/editor/editor_vcs_interface.cpp b/editor/editor_vcs_interface.cpp
index f4745b8730..6a5d49e41a 100644
--- a/editor/editor_vcs_interface.cpp
+++ b/editor/editor_vcs_interface.cpp
@@ -41,17 +41,17 @@ void EditorVCSInterface::popup_error(const String &p_msg) {
bool EditorVCSInterface::initialize(const String &p_project_path) {
bool result = false;
- GDVIRTUAL_REQUIRED_CALL(_initialize, p_project_path, result);
+ GDVIRTUAL_CALL(_initialize, p_project_path, result);
return result;
}
void EditorVCSInterface::set_credentials(const String &p_username, const String &p_password, const String &p_ssh_public_key, const String &p_ssh_private_key, const String &p_ssh_passphrase) {
- GDVIRTUAL_REQUIRED_CALL(_set_credentials, p_username, p_password, p_ssh_public_key, p_ssh_private_key, p_ssh_passphrase);
+ GDVIRTUAL_CALL(_set_credentials, p_username, p_password, p_ssh_public_key, p_ssh_private_key, p_ssh_passphrase);
}
List<String> EditorVCSInterface::get_remotes() {
TypedArray<String> result;
- if (!GDVIRTUAL_REQUIRED_CALL(_get_remotes, result)) {
+ if (!GDVIRTUAL_CALL(_get_remotes, result)) {
return {};
}
@@ -64,7 +64,7 @@ List<String> EditorVCSInterface::get_remotes() {
List<EditorVCSInterface::StatusFile> EditorVCSInterface::get_modified_files_data() {
TypedArray<Dictionary> result;
- if (!GDVIRTUAL_REQUIRED_CALL(_get_modified_files_data, result)) {
+ if (!GDVIRTUAL_CALL(_get_modified_files_data, result)) {
return {};
}
@@ -76,24 +76,24 @@ List<EditorVCSInterface::StatusFile> EditorVCSInterface::get_modified_files_data
}
void EditorVCSInterface::stage_file(const String &p_file_path) {
- GDVIRTUAL_REQUIRED_CALL(_stage_file, p_file_path);
+ GDVIRTUAL_CALL(_stage_file, p_file_path);
}
void EditorVCSInterface::unstage_file(const String &p_file_path) {
- GDVIRTUAL_REQUIRED_CALL(_unstage_file, p_file_path);
+ GDVIRTUAL_CALL(_unstage_file, p_file_path);
}
void EditorVCSInterface::discard_file(const String &p_file_path) {
- GDVIRTUAL_REQUIRED_CALL(_discard_file, p_file_path);
+ GDVIRTUAL_CALL(_discard_file, p_file_path);
}
void EditorVCSInterface::commit(const String &p_msg) {
- GDVIRTUAL_REQUIRED_CALL(_commit, p_msg);
+ GDVIRTUAL_CALL(_commit, p_msg);
}
List<EditorVCSInterface::DiffFile> EditorVCSInterface::get_diff(const String &p_identifier, TreeArea p_area) {
TypedArray<Dictionary> result;
- if (!GDVIRTUAL_REQUIRED_CALL(_get_diff, p_identifier, int(p_area), result)) {
+ if (!GDVIRTUAL_CALL(_get_diff, p_identifier, int(p_area), result)) {
return {};
}
@@ -106,7 +106,7 @@ List<EditorVCSInterface::DiffFile> EditorVCSInterface::get_diff(const String &p_
List<EditorVCSInterface::Commit> EditorVCSInterface::get_previous_commits(int p_max_commits) {
TypedArray<Dictionary> result;
- if (!GDVIRTUAL_REQUIRED_CALL(_get_previous_commits, p_max_commits, result)) {
+ if (!GDVIRTUAL_CALL(_get_previous_commits, p_max_commits, result)) {
return {};
}
@@ -119,7 +119,7 @@ List<EditorVCSInterface::Commit> EditorVCSInterface::get_previous_commits(int p_
List<String> EditorVCSInterface::get_branch_list() {
TypedArray<String> result;
- if (!GDVIRTUAL_REQUIRED_CALL(_get_branch_list, result)) {
+ if (!GDVIRTUAL_CALL(_get_branch_list, result)) {
return {};
}
@@ -131,48 +131,48 @@ List<String> EditorVCSInterface::get_branch_list() {
}
void EditorVCSInterface::create_branch(const String &p_branch_name) {
- GDVIRTUAL_REQUIRED_CALL(_create_branch, p_branch_name);
+ GDVIRTUAL_CALL(_create_branch, p_branch_name);
}
void EditorVCSInterface::create_remote(const String &p_remote_name, const String &p_remote_url) {
- GDVIRTUAL_REQUIRED_CALL(_create_remote, p_remote_name, p_remote_url);
+ GDVIRTUAL_CALL(_create_remote, p_remote_name, p_remote_url);
}
void EditorVCSInterface::remove_branch(const String &p_branch_name) {
- GDVIRTUAL_REQUIRED_CALL(_remove_branch, p_branch_name);
+ GDVIRTUAL_CALL(_remove_branch, p_branch_name);
}
void EditorVCSInterface::remove_remote(const String &p_remote_name) {
- GDVIRTUAL_REQUIRED_CALL(_remove_remote, p_remote_name);
+ GDVIRTUAL_CALL(_remove_remote, p_remote_name);
}
String EditorVCSInterface::get_current_branch_name() {
String result;
- GDVIRTUAL_REQUIRED_CALL(_get_current_branch_name, result);
+ GDVIRTUAL_CALL(_get_current_branch_name, result);
return result;
}
bool EditorVCSInterface::checkout_branch(const String &p_branch_name) {
bool result = false;
- GDVIRTUAL_REQUIRED_CALL(_checkout_branch, p_branch_name, result);
+ GDVIRTUAL_CALL(_checkout_branch, p_branch_name, result);
return result;
}
void EditorVCSInterface::pull(const String &p_remote) {
- GDVIRTUAL_REQUIRED_CALL(_pull, p_remote);
+ GDVIRTUAL_CALL(_pull, p_remote);
}
void EditorVCSInterface::push(const String &p_remote, bool p_force) {
- GDVIRTUAL_REQUIRED_CALL(_push, p_remote, p_force);
+ GDVIRTUAL_CALL(_push, p_remote, p_force);
}
void EditorVCSInterface::fetch(const String &p_remote) {
- GDVIRTUAL_REQUIRED_CALL(_fetch, p_remote);
+ GDVIRTUAL_CALL(_fetch, p_remote);
}
List<EditorVCSInterface::DiffHunk> EditorVCSInterface::get_line_diff(const String &p_file_path, const String &p_text) {
TypedArray<Dictionary> result;
- if (!GDVIRTUAL_REQUIRED_CALL(_get_line_diff, p_file_path, p_text, result)) {
+ if (!GDVIRTUAL_CALL(_get_line_diff, p_file_path, p_text, result)) {
return {};
}
@@ -185,13 +185,13 @@ List<EditorVCSInterface::DiffHunk> EditorVCSInterface::get_line_diff(const Strin
bool EditorVCSInterface::shut_down() {
bool result = false;
- GDVIRTUAL_REQUIRED_CALL(_shut_down, result);
+ GDVIRTUAL_CALL(_shut_down, result);
return result;
}
String EditorVCSInterface::get_vcs_name() {
String result;
- GDVIRTUAL_REQUIRED_CALL(_get_vcs_name, result);
+ GDVIRTUAL_CALL(_get_vcs_name, result);
return result;
}
diff --git a/editor/editor_vcs_interface.h b/editor/editor_vcs_interface.h
index 8fcd45756a..a425682bed 100644
--- a/editor/editor_vcs_interface.h
+++ b/editor/editor_vcs_interface.h
@@ -106,29 +106,29 @@ protected:
StatusFile _convert_status_file(const Dictionary &p_status_file);
// Proxy endpoints for extensions to implement
- GDVIRTUAL1R(bool, _initialize, String);
- GDVIRTUAL5(_set_credentials, String, String, String, String, String);
- GDVIRTUAL0R(TypedArray<Dictionary>, _get_modified_files_data);
- GDVIRTUAL1(_stage_file, String);
- GDVIRTUAL1(_unstage_file, String);
- GDVIRTUAL1(_discard_file, String);
- GDVIRTUAL1(_commit, String);
- GDVIRTUAL2R(TypedArray<Dictionary>, _get_diff, String, int);
- GDVIRTUAL0R(bool, _shut_down);
- GDVIRTUAL0R(String, _get_vcs_name);
- GDVIRTUAL1R(TypedArray<Dictionary>, _get_previous_commits, int);
- GDVIRTUAL0R(TypedArray<String>, _get_branch_list);
- GDVIRTUAL0R(TypedArray<String>, _get_remotes);
- GDVIRTUAL1(_create_branch, String);
- GDVIRTUAL1(_remove_branch, String);
- GDVIRTUAL2(_create_remote, String, String);
- GDVIRTUAL1(_remove_remote, String);
- GDVIRTUAL0R(String, _get_current_branch_name);
- GDVIRTUAL1R(bool, _checkout_branch, String);
- GDVIRTUAL1(_pull, String);
- GDVIRTUAL2(_push, String, bool);
- GDVIRTUAL1(_fetch, String);
- GDVIRTUAL2R(TypedArray<Dictionary>, _get_line_diff, String, String);
+ GDVIRTUAL1R_REQUIRED(bool, _initialize, String);
+ GDVIRTUAL5_REQUIRED(_set_credentials, String, String, String, String, String);
+ GDVIRTUAL0R_REQUIRED(TypedArray<Dictionary>, _get_modified_files_data);
+ GDVIRTUAL1_REQUIRED(_stage_file, String);
+ GDVIRTUAL1_REQUIRED(_unstage_file, String);
+ GDVIRTUAL1_REQUIRED(_discard_file, String);
+ GDVIRTUAL1_REQUIRED(_commit, String);
+ GDVIRTUAL2R_REQUIRED(TypedArray<Dictionary>, _get_diff, String, int);
+ GDVIRTUAL0R_REQUIRED(bool, _shut_down);
+ GDVIRTUAL0R_REQUIRED(String, _get_vcs_name);
+ GDVIRTUAL1R_REQUIRED(TypedArray<Dictionary>, _get_previous_commits, int);
+ GDVIRTUAL0R_REQUIRED(TypedArray<String>, _get_branch_list);
+ GDVIRTUAL0R_REQUIRED(TypedArray<String>, _get_remotes);
+ GDVIRTUAL1_REQUIRED(_create_branch, String);
+ GDVIRTUAL1_REQUIRED(_remove_branch, String);
+ GDVIRTUAL2_REQUIRED(_create_remote, String, String);
+ GDVIRTUAL1_REQUIRED(_remove_remote, String);
+ GDVIRTUAL0R_REQUIRED(String, _get_current_branch_name);
+ GDVIRTUAL1R_REQUIRED(bool, _checkout_branch, String);
+ GDVIRTUAL1_REQUIRED(_pull, String);
+ GDVIRTUAL2_REQUIRED(_push, String, bool);
+ GDVIRTUAL1_REQUIRED(_fetch, String);
+ GDVIRTUAL2R_REQUIRED(TypedArray<Dictionary>, _get_line_diff, String, String);
public:
static EditorVCSInterface *get_singleton();
diff --git a/editor/export/editor_export_platform_extension.cpp b/editor/export/editor_export_platform_extension.cpp
index 5b75ff19a4..b4f338437f 100644
--- a/editor/export/editor_export_platform_extension.cpp
+++ b/editor/export/editor_export_platform_extension.cpp
@@ -81,7 +81,7 @@ void EditorExportPlatformExtension::_bind_methods() {
void EditorExportPlatformExtension::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const {
Vector<String> ret;
- if (GDVIRTUAL_REQUIRED_CALL(_get_preset_features, p_preset, ret) && r_features) {
+ if (GDVIRTUAL_CALL(_get_preset_features, p_preset, ret) && r_features) {
for (const String &E : ret) {
r_features->push_back(E);
}
@@ -144,19 +144,19 @@ String EditorExportPlatformExtension::get_export_option_warning(const EditorExpo
String EditorExportPlatformExtension::get_os_name() const {
String ret;
- GDVIRTUAL_REQUIRED_CALL(_get_os_name, ret);
+ GDVIRTUAL_CALL(_get_os_name, ret);
return ret;
}
String EditorExportPlatformExtension::get_name() const {
String ret;
- GDVIRTUAL_REQUIRED_CALL(_get_name, ret);
+ GDVIRTUAL_CALL(_get_name, ret);
return ret;
}
Ref<Texture2D> EditorExportPlatformExtension::get_logo() const {
Ref<Texture2D> ret;
- GDVIRTUAL_REQUIRED_CALL(_get_logo, ret);
+ GDVIRTUAL_CALL(_get_logo, ret);
return ret;
}
@@ -238,7 +238,7 @@ bool EditorExportPlatformExtension::has_valid_export_configuration(const Ref<Edi
bool ret = false;
config_error = r_error;
config_missing_templates = r_missing_templates;
- if (GDVIRTUAL_REQUIRED_CALL(_has_valid_export_configuration, p_preset, p_debug, ret)) {
+ if (GDVIRTUAL_CALL(_has_valid_export_configuration, p_preset, p_debug, ret)) {
r_error = config_error;
r_missing_templates = config_missing_templates;
}
@@ -248,7 +248,7 @@ bool EditorExportPlatformExtension::has_valid_export_configuration(const Ref<Edi
bool EditorExportPlatformExtension::has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const {
bool ret = false;
config_error = r_error;
- if (GDVIRTUAL_REQUIRED_CALL(_has_valid_project_configuration, p_preset, ret)) {
+ if (GDVIRTUAL_CALL(_has_valid_project_configuration, p_preset, ret)) {
r_error = config_error;
}
return ret;
@@ -257,7 +257,7 @@ bool EditorExportPlatformExtension::has_valid_project_configuration(const Ref<Ed
List<String> EditorExportPlatformExtension::get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const {
List<String> ret_list;
Vector<String> ret;
- if (GDVIRTUAL_REQUIRED_CALL(_get_binary_extensions, p_preset, ret)) {
+ if (GDVIRTUAL_CALL(_get_binary_extensions, p_preset, ret)) {
for (const String &E : ret) {
ret_list.push_back(E);
}
@@ -269,7 +269,7 @@ Error EditorExportPlatformExtension::export_project(const Ref<EditorExportPreset
ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
Error ret = FAILED;
- GDVIRTUAL_REQUIRED_CALL(_export_project, p_preset, p_debug, p_path, p_flags, ret);
+ GDVIRTUAL_CALL(_export_project, p_preset, p_debug, p_path, p_flags, ret);
return ret;
}
@@ -333,7 +333,7 @@ Error EditorExportPlatformExtension::export_zip_patch(const Ref<EditorExportPres
void EditorExportPlatformExtension::get_platform_features(List<String> *r_features) const {
Vector<String> ret;
- if (GDVIRTUAL_REQUIRED_CALL(_get_platform_features, ret) && r_features) {
+ if (GDVIRTUAL_CALL(_get_platform_features, ret) && r_features) {
for (const String &E : ret) {
r_features->push_back(E);
}
diff --git a/editor/export/editor_export_platform_extension.h b/editor/export/editor_export_platform_extension.h
index d1db922698..390aaf4590 100644
--- a/editor/export/editor_export_platform_extension.h
+++ b/editor/export/editor_export_platform_extension.h
@@ -45,7 +45,7 @@ protected:
public:
virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) const override;
- GDVIRTUAL1RC(Vector<String>, _get_preset_features, Ref<EditorExportPreset>);
+ GDVIRTUAL1RC_REQUIRED(Vector<String>, _get_preset_features, Ref<EditorExportPreset>);
virtual bool is_executable(const String &p_path) const override;
GDVIRTUAL1RC(bool, _is_executable, const String &);
@@ -63,13 +63,13 @@ public:
GDVIRTUAL2RC(String, _get_export_option_warning, Ref<EditorExportPreset>, const StringName &);
virtual String get_os_name() const override;
- GDVIRTUAL0RC(String, _get_os_name);
+ GDVIRTUAL0RC_REQUIRED(String, _get_os_name);
virtual String get_name() const override;
- GDVIRTUAL0RC(String, _get_name);
+ GDVIRTUAL0RC_REQUIRED(String, _get_name);
virtual Ref<Texture2D> get_logo() const override;
- GDVIRTUAL0RC(Ref<Texture2D>, _get_logo);
+ GDVIRTUAL0RC_REQUIRED(Ref<Texture2D>, _get_logo);
virtual bool poll_export() override;
GDVIRTUAL0R(bool, _poll_export);
@@ -119,16 +119,16 @@ public:
GDVIRTUAL2RC(bool, _can_export, Ref<EditorExportPreset>, bool);
virtual bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug = false) const override;
- GDVIRTUAL2RC(bool, _has_valid_export_configuration, Ref<EditorExportPreset>, bool);
+ GDVIRTUAL2RC_REQUIRED(bool, _has_valid_export_configuration, Ref<EditorExportPreset>, bool);
virtual bool has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const override;
- GDVIRTUAL1RC(bool, _has_valid_project_configuration, Ref<EditorExportPreset>);
+ GDVIRTUAL1RC_REQUIRED(bool, _has_valid_project_configuration, Ref<EditorExportPreset>);
virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override;
- GDVIRTUAL1RC(Vector<String>, _get_binary_extensions, Ref<EditorExportPreset>);
+ GDVIRTUAL1RC_REQUIRED(Vector<String>, _get_binary_extensions, Ref<EditorExportPreset>);
virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags = 0) override;
- GDVIRTUAL4R(Error, _export_project, Ref<EditorExportPreset>, bool, const String &, BitField<EditorExportPlatform::DebugFlags>);
+ GDVIRTUAL4R_REQUIRED(Error, _export_project, Ref<EditorExportPreset>, bool, const String &, BitField<EditorExportPlatform::DebugFlags>);
virtual Error export_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags = 0) override;
GDVIRTUAL4R(Error, _export_pack, Ref<EditorExportPreset>, bool, const String &, BitField<EditorExportPlatform::DebugFlags>);
@@ -143,7 +143,7 @@ public:
GDVIRTUAL5R(Error, _export_zip_patch, Ref<EditorExportPreset>, bool, const String &, const Vector<String> &, BitField<EditorExportPlatform::DebugFlags>);
virtual void get_platform_features(List<String> *r_features) const override;
- GDVIRTUAL0RC(Vector<String>, _get_platform_features);
+ GDVIRTUAL0RC_REQUIRED(Vector<String>, _get_platform_features);
virtual String get_debug_protocol() const override;
GDVIRTUAL0RC(String, _get_debug_protocol);
diff --git a/editor/export/editor_export_plugin.cpp b/editor/export/editor_export_plugin.cpp
index 5945c02413..2f57010c2c 100644
--- a/editor/export/editor_export_plugin.cpp
+++ b/editor/export/editor_export_plugin.cpp
@@ -187,7 +187,7 @@ bool EditorExportPlugin::_begin_customize_resources(const Ref<EditorExportPlatfo
Ref<Resource> EditorExportPlugin::_customize_resource(const Ref<Resource> &p_resource, const String &p_path) {
Ref<Resource> ret;
- GDVIRTUAL_REQUIRED_CALL(_customize_resource, p_resource, p_path, ret);
+ GDVIRTUAL_CALL(_customize_resource, p_resource, p_path, ret);
return ret;
}
@@ -199,13 +199,13 @@ bool EditorExportPlugin::_begin_customize_scenes(const Ref<EditorExportPlatform>
Node *EditorExportPlugin::_customize_scene(Node *p_root, const String &p_path) {
Node *ret = nullptr;
- GDVIRTUAL_REQUIRED_CALL(_customize_scene, p_root, p_path, ret);
+ GDVIRTUAL_CALL(_customize_scene, p_root, p_path, ret);
return ret;
}
uint64_t EditorExportPlugin::_get_customization_configuration_hash() const {
uint64_t ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_get_customization_configuration_hash, ret);
+ GDVIRTUAL_CALL(_get_customization_configuration_hash, ret);
return ret;
}
@@ -219,7 +219,7 @@ void EditorExportPlugin::_end_customize_resources() {
String EditorExportPlugin::get_name() const {
String ret;
- GDVIRTUAL_REQUIRED_CALL(_get_name, ret);
+ GDVIRTUAL_CALL(_get_name, ret);
return ret;
}
diff --git a/editor/export/editor_export_plugin.h b/editor/export/editor_export_plugin.h
index 4c0107af72..8c744b3108 100644
--- a/editor/export/editor_export_plugin.h
+++ b/editor/export/editor_export_plugin.h
@@ -119,11 +119,11 @@ protected:
GDVIRTUAL0(_export_end)
GDVIRTUAL2RC(bool, _begin_customize_resources, const Ref<EditorExportPlatform> &, const Vector<String> &)
- GDVIRTUAL2R(Ref<Resource>, _customize_resource, const Ref<Resource> &, String)
+ GDVIRTUAL2R_REQUIRED(Ref<Resource>, _customize_resource, const Ref<Resource> &, String)
GDVIRTUAL2RC(bool, _begin_customize_scenes, const Ref<EditorExportPlatform> &, const Vector<String> &)
- GDVIRTUAL2R(Node *, _customize_scene, Node *, String)
- GDVIRTUAL0RC(uint64_t, _get_customization_configuration_hash)
+ GDVIRTUAL2R_REQUIRED(Node *, _customize_scene, Node *, String)
+ GDVIRTUAL0RC_REQUIRED(uint64_t, _get_customization_configuration_hash)
GDVIRTUAL0(_end_customize_scenes)
GDVIRTUAL0(_end_customize_resources)
@@ -134,7 +134,7 @@ protected:
GDVIRTUAL1RC(bool, _should_update_export_options, const Ref<EditorExportPlatform> &);
GDVIRTUAL2RC(String, _get_export_option_warning, const Ref<EditorExportPlatform> &, String);
- GDVIRTUAL0RC(String, _get_name)
+ GDVIRTUAL0RC_REQUIRED(String, _get_name)
GDVIRTUAL1RC(bool, _supports_platform, const Ref<EditorExportPlatform> &);
diff --git a/modules/squish/image_decompress_squish.cpp b/editor/file_info.cpp
index 3841ba8db1..04d82547ab 100644
--- a/modules/squish/image_decompress_squish.cpp
+++ b/editor/file_info.cpp
@@ -1,5 +1,5 @@
/**************************************************************************/
-/* image_decompress_squish.cpp */
+/* file_info.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,69 +28,34 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
-#include "image_decompress_squish.h"
+#include "editor/file_info.h"
-#include <squish.h>
-
-void image_decompress_squish(Image *p_image) {
- int w = p_image->get_width();
- int h = p_image->get_height();
-
- Image::Format source_format = p_image->get_format();
- Image::Format target_format = Image::FORMAT_RGBA8;
-
- Vector<uint8_t> data;
- int64_t target_size = Image::get_image_data_size(w, h, target_format, p_image->has_mipmaps());
- int mm_count = p_image->get_mipmap_count();
- data.resize(target_size);
-
- const uint8_t *rb = p_image->get_data().ptr();
- uint8_t *wb = data.ptrw();
-
- int squish_flags = 0;
-
- switch (source_format) {
- case Image::FORMAT_DXT1:
- squish_flags = squish::kDxt1;
+void sort_file_info_list(List<FileInfo> &r_file_list, FileSortOption p_file_sort_option) {
+ // Sort the file list if needed.
+ switch (p_file_sort_option) {
+ case FileSortOption::FILE_SORT_TYPE:
+ r_file_list.sort_custom<FileInfoTypeComparator>();
break;
-
- case Image::FORMAT_DXT3:
- squish_flags = squish::kDxt3;
+ case FileSortOption::FILE_SORT_TYPE_REVERSE:
+ r_file_list.sort_custom<FileInfoTypeComparator>();
+ r_file_list.reverse();
break;
-
- case Image::FORMAT_DXT5:
- case Image::FORMAT_DXT5_RA_AS_RG:
- squish_flags = squish::kDxt5;
+ case FileSortOption::FILE_SORT_MODIFIED_TIME:
+ r_file_list.sort_custom<FileInfoModifiedTimeComparator>();
break;
-
- case Image::FORMAT_RGTC_R:
- squish_flags = squish::kBc4;
+ case FileSortOption::FILE_SORT_MODIFIED_TIME_REVERSE:
+ r_file_list.sort_custom<FileInfoModifiedTimeComparator>();
+ r_file_list.reverse();
break;
-
- case Image::FORMAT_RGTC_RG:
- squish_flags = squish::kBc5;
+ case FileSortOption::FILE_SORT_NAME_REVERSE:
+ r_file_list.sort();
+ r_file_list.reverse();
+ break;
+ case FileSortOption::FILE_SORT_NAME:
+ r_file_list.sort();
break;
-
default:
- ERR_FAIL_MSG("Squish: Can't decompress unknown format: " + itos(p_image->get_format()) + ".");
+ ERR_FAIL_MSG("Invalid file sort option.");
break;
}
-
- for (int i = 0; i <= mm_count; i++) {
- int64_t src_ofs = 0, mipmap_size = 0;
- int mipmap_w = 0, mipmap_h = 0;
- p_image->get_mipmap_offset_size_and_dimensions(i, src_ofs, mipmap_size, mipmap_w, mipmap_h);
-
- int64_t dst_ofs = Image::get_image_mipmap_offset(p_image->get_width(), p_image->get_height(), target_format, i);
- squish::DecompressImage(&wb[dst_ofs], w, h, &rb[src_ofs], squish_flags);
-
- w >>= 1;
- h >>= 1;
- }
-
- p_image->set_data(p_image->get_width(), p_image->get_height(), p_image->has_mipmaps(), target_format, data);
-
- if (source_format == Image::FORMAT_DXT5_RA_AS_RG) {
- p_image->convert_ra_rgba8_to_rg();
- }
}
diff --git a/editor/file_info.h b/editor/file_info.h
new file mode 100644
index 0000000000..2ce521992c
--- /dev/null
+++ b/editor/file_info.h
@@ -0,0 +1,74 @@
+/**************************************************************************/
+/* file_info.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 FILE_INFO_H
+#define FILE_INFO_H
+
+#include "core/variant/variant.h"
+
+enum class FileSortOption {
+ FILE_SORT_NAME = 0,
+ FILE_SORT_NAME_REVERSE = 1,
+ FILE_SORT_TYPE = 2,
+ FILE_SORT_TYPE_REVERSE = 3,
+ FILE_SORT_MODIFIED_TIME = 4,
+ FILE_SORT_MODIFIED_TIME_REVERSE = 5,
+ FILE_SORT_MAX = 6,
+};
+
+struct FileInfo {
+ String name;
+ String path;
+ String icon_path;
+ StringName type;
+ Vector<String> sources;
+ bool import_broken = false;
+ uint64_t modified_time = 0;
+
+ bool operator<(const FileInfo &p_fi) const {
+ return FileNoCaseComparator()(name, p_fi.name);
+ }
+};
+
+struct FileInfoTypeComparator {
+ bool operator()(const FileInfo &p_a, const FileInfo &p_b) const {
+ return FileNoCaseComparator()(p_a.name.get_extension() + p_a.type + p_a.name.get_basename(), p_b.name.get_extension() + p_b.type + p_b.name.get_basename());
+ }
+};
+
+struct FileInfoModifiedTimeComparator {
+ bool operator()(const FileInfo &p_a, const FileInfo &p_b) const {
+ return p_a.modified_time > p_b.modified_time;
+ }
+};
+
+void sort_file_info_list(List<FileInfo> &r_file_list, FileSortOption p_file_sort_option);
+
+#endif // FILE_INFO_H
diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp
index dd2eec6893..c9cc42d68d 100644
--- a/editor/filesystem_dock.cpp
+++ b/editor/filesystem_dock.cpp
@@ -263,7 +263,7 @@ bool FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory
}
// Create items for all subdirectories.
- bool reversed = file_sort == FILE_SORT_NAME_REVERSE;
+ bool reversed = file_sort == FileSortOption::FILE_SORT_NAME_REVERSE;
for (int i = reversed ? p_dir->get_subdir_count() - 1 : 0;
reversed ? i >= 0 : i < p_dir->get_subdir_count();
reversed ? i-- : i++) {
@@ -294,28 +294,28 @@ bool FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory
}
}
- FileInfo fi;
- fi.name = p_dir->get_file(i);
- fi.type = p_dir->get_file_type(i);
- fi.icon_path = p_dir->get_file_icon_path(i);
- fi.import_broken = !p_dir->get_file_import_is_valid(i);
- fi.modified_time = p_dir->get_file_modified_time(i);
+ FileInfo file_info;
+ file_info.name = p_dir->get_file(i);
+ file_info.type = p_dir->get_file_type(i);
+ file_info.icon_path = p_dir->get_file_icon_path(i);
+ file_info.import_broken = !p_dir->get_file_import_is_valid(i);
+ file_info.modified_time = p_dir->get_file_modified_time(i);
- file_list.push_back(fi);
+ file_list.push_back(file_info);
}
// Sort the file list if needed.
- _sort_file_info_list(file_list);
+ sort_file_info_list(file_list, file_sort);
// Build the tree.
const int icon_size = get_theme_constant(SNAME("class_icon_size"), EditorStringName(Editor));
- for (const FileInfo &fi : file_list) {
+ for (const FileInfo &file_info : file_list) {
TreeItem *file_item = tree->create_item(subdirectory_item);
- const String file_metadata = lpath.path_join(fi.name);
- file_item->set_text(0, fi.name);
+ const String file_metadata = lpath.path_join(file_info.name);
+ file_item->set_text(0, file_info.name);
file_item->set_structured_text_bidi_override(0, TextServer::STRUCTURED_TEXT_FILE);
- file_item->set_icon(0, _get_tree_item_icon(!fi.import_broken, fi.type, fi.icon_path));
+ file_item->set_icon(0, _get_tree_item_icon(!file_info.import_broken, file_info.type, file_info.icon_path));
if (da->is_link(file_metadata)) {
file_item->set_icon_overlay(0, get_editor_theme_icon(SNAME("LinkOverlay")));
file_item->set_tooltip_text(0, vformat(TTR("Link to: %s"), da->read_link(file_metadata)));
@@ -860,19 +860,19 @@ void FileSystemDock::_search(EditorFileSystemDirectory *p_path, List<FileInfo> *
String file = p_path->get_file(i);
if (_matches_all_search_tokens(file)) {
- FileInfo fi;
- fi.name = file;
- fi.type = p_path->get_file_type(i);
- fi.path = p_path->get_file_path(i);
- fi.import_broken = !p_path->get_file_import_is_valid(i);
- fi.modified_time = p_path->get_file_modified_time(i);
-
- if (_is_file_type_disabled_by_feature_profile(fi.type)) {
+ FileInfo file_info;
+ file_info.name = file;
+ file_info.type = p_path->get_file_type(i);
+ file_info.path = p_path->get_file_path(i);
+ file_info.import_broken = !p_path->get_file_import_is_valid(i);
+ file_info.modified_time = p_path->get_file_modified_time(i);
+
+ if (_is_file_type_disabled_by_feature_profile(file_info.type)) {
// This type is disabled, will not appear here.
continue;
}
- matches->push_back(fi);
+ matches->push_back(file_info);
if (matches->size() > p_max_items) {
return;
}
@@ -880,45 +880,6 @@ void FileSystemDock::_search(EditorFileSystemDirectory *p_path, List<FileInfo> *
}
}
-struct FileSystemDock::FileInfoTypeComparator {
- bool operator()(const FileInfo &p_a, const FileInfo &p_b) const {
- return FileNoCaseComparator()(p_a.name.get_extension() + p_a.type + p_a.name.get_basename(), p_b.name.get_extension() + p_b.type + p_b.name.get_basename());
- }
-};
-
-struct FileSystemDock::FileInfoModifiedTimeComparator {
- bool operator()(const FileInfo &p_a, const FileInfo &p_b) const {
- return p_a.modified_time > p_b.modified_time;
- }
-};
-
-void FileSystemDock::_sort_file_info_list(List<FileSystemDock::FileInfo> &r_file_list) {
- // Sort the file list if needed.
- switch (file_sort) {
- case FILE_SORT_TYPE:
- r_file_list.sort_custom<FileInfoTypeComparator>();
- break;
- case FILE_SORT_TYPE_REVERSE:
- r_file_list.sort_custom<FileInfoTypeComparator>();
- r_file_list.reverse();
- break;
- case FILE_SORT_MODIFIED_TIME:
- r_file_list.sort_custom<FileInfoModifiedTimeComparator>();
- break;
- case FILE_SORT_MODIFIED_TIME_REVERSE:
- r_file_list.sort_custom<FileInfoModifiedTimeComparator>();
- r_file_list.reverse();
- break;
- case FILE_SORT_NAME_REVERSE:
- r_file_list.sort();
- r_file_list.reverse();
- break;
- default: // FILE_SORT_NAME
- r_file_list.sort();
- break;
- }
-}
-
void FileSystemDock::_update_file_list(bool p_keep_selection) {
// Register the previously current and selected items.
HashSet<String> previous_selection;
@@ -1005,22 +966,22 @@ void FileSystemDock::_update_file_list(bool p_keep_selection) {
int index;
EditorFileSystemDirectory *efd = EditorFileSystem::get_singleton()->find_file(favorite, &index);
- FileInfo fi;
- fi.name = favorite.get_file();
- fi.path = favorite;
+ FileInfo file_info;
+ file_info.name = favorite.get_file();
+ file_info.path = favorite;
if (efd) {
- fi.type = efd->get_file_type(index);
- fi.icon_path = efd->get_file_icon_path(index);
- fi.import_broken = !efd->get_file_import_is_valid(index);
- fi.modified_time = efd->get_file_modified_time(index);
+ file_info.type = efd->get_file_type(index);
+ file_info.icon_path = efd->get_file_icon_path(index);
+ file_info.import_broken = !efd->get_file_import_is_valid(index);
+ file_info.modified_time = efd->get_file_modified_time(index);
} else {
- fi.type = "";
- fi.import_broken = true;
- fi.modified_time = 0;
+ file_info.type = "";
+ file_info.import_broken = true;
+ file_info.modified_time = 0;
}
- if (searched_tokens.is_empty() || _matches_all_search_tokens(fi.name)) {
- file_list.push_back(fi);
+ if (searched_tokens.is_empty() || _matches_all_search_tokens(file_info.name)) {
+ file_list.push_back(file_info);
}
}
}
@@ -1077,7 +1038,7 @@ void FileSystemDock::_update_file_list(bool p_keep_selection) {
files->set_item_icon_modulate(-1, editor_is_dark_theme ? inherited_folder_color : inherited_folder_color * ITEM_COLOR_SCALE);
}
- bool reversed = file_sort == FILE_SORT_NAME_REVERSE;
+ bool reversed = file_sort == FileSortOption::FILE_SORT_NAME_REVERSE;
for (int i = reversed ? efd->get_subdir_count() - 1 : 0;
reversed ? i >= 0 : i < efd->get_subdir_count();
reversed ? i-- : i++) {
@@ -1099,21 +1060,21 @@ void FileSystemDock::_update_file_list(bool p_keep_selection) {
// Display the folder content.
for (int i = 0; i < efd->get_file_count(); i++) {
- FileInfo fi;
- fi.name = efd->get_file(i);
- fi.path = directory.path_join(fi.name);
- fi.type = efd->get_file_type(i);
- fi.icon_path = efd->get_file_icon_path(i);
- fi.import_broken = !efd->get_file_import_is_valid(i);
- fi.modified_time = efd->get_file_modified_time(i);
+ FileInfo file_info;
+ file_info.name = efd->get_file(i);
+ file_info.path = directory.path_join(file_info.name);
+ file_info.type = efd->get_file_type(i);
+ file_info.icon_path = efd->get_file_icon_path(i);
+ file_info.import_broken = !efd->get_file_import_is_valid(i);
+ file_info.modified_time = efd->get_file_modified_time(i);
- file_list.push_back(fi);
+ file_list.push_back(file_info);
}
}
}
// Sort the file list if needed.
- _sort_file_info_list(file_list);
+ sort_file_info_list(file_list, file_sort);
// Fills the ItemList control node from the FileInfos.
String main_scene = GLOBAL_GET("application/run/main_scene");
@@ -3935,7 +3896,7 @@ void FileSystemDock::_project_settings_changed() {
}
void FileSystemDock::set_file_sort(FileSortOption p_file_sort) {
- for (int i = 0; i != FILE_SORT_MAX; i++) {
+ for (int i = 0; i != (int)FileSortOption::FILE_SORT_MAX; i++) {
tree_button_sort->get_popup()->set_item_checked(i, (i == (int)p_file_sort));
file_list_button_sort->get_popup()->set_item_checked(i, (i == (int)p_file_sort));
}
@@ -3965,13 +3926,13 @@ MenuButton *FileSystemDock::_create_file_menu_button() {
PopupMenu *p = button->get_popup();
p->connect(SceneStringName(id_pressed), callable_mp(this, &FileSystemDock::_file_sort_popup));
- p->add_radio_check_item(TTR("Sort by Name (Ascending)"), FILE_SORT_NAME);
- p->add_radio_check_item(TTR("Sort by Name (Descending)"), FILE_SORT_NAME_REVERSE);
- p->add_radio_check_item(TTR("Sort by Type (Ascending)"), FILE_SORT_TYPE);
- p->add_radio_check_item(TTR("Sort by Type (Descending)"), FILE_SORT_TYPE_REVERSE);
- p->add_radio_check_item(TTR("Sort by Last Modified"), FILE_SORT_MODIFIED_TIME);
- p->add_radio_check_item(TTR("Sort by First Modified"), FILE_SORT_MODIFIED_TIME_REVERSE);
- p->set_item_checked(file_sort, true);
+ p->add_radio_check_item(TTR("Sort by Name (Ascending)"), (int)FileSortOption::FILE_SORT_NAME);
+ p->add_radio_check_item(TTR("Sort by Name (Descending)"), (int)FileSortOption::FILE_SORT_NAME_REVERSE);
+ p->add_radio_check_item(TTR("Sort by Type (Ascending)"), (int)FileSortOption::FILE_SORT_TYPE);
+ p->add_radio_check_item(TTR("Sort by Type (Descending)"), (int)FileSortOption::FILE_SORT_TYPE_REVERSE);
+ p->add_radio_check_item(TTR("Sort by Last Modified"), (int)FileSortOption::FILE_SORT_MODIFIED_TIME);
+ p->add_radio_check_item(TTR("Sort by First Modified"), (int)FileSortOption::FILE_SORT_MODIFIED_TIME_REVERSE);
+ p->set_item_checked((int)file_sort, true);
return button;
}
@@ -4041,7 +4002,7 @@ void FileSystemDock::save_layout_to_config(Ref<ConfigFile> p_layout, const Strin
p_layout->set_value(p_section, "dock_filesystem_h_split_offset", get_h_split_offset());
p_layout->set_value(p_section, "dock_filesystem_v_split_offset", get_v_split_offset());
p_layout->set_value(p_section, "dock_filesystem_display_mode", get_display_mode());
- p_layout->set_value(p_section, "dock_filesystem_file_sort", get_file_sort());
+ p_layout->set_value(p_section, "dock_filesystem_file_sort", (int)get_file_sort());
p_layout->set_value(p_section, "dock_filesystem_file_list_display_mode", get_file_list_display_mode());
PackedStringArray selected_files = get_selected_paths();
p_layout->set_value(p_section, "dock_filesystem_selected_paths", selected_files);
@@ -4142,22 +4103,25 @@ FileSystemDock::FileSystemDock() {
add_child(top_vbc);
HBoxContainer *toolbar_hbc = memnew(HBoxContainer);
- toolbar_hbc->add_theme_constant_override("separation", 0);
top_vbc->add_child(toolbar_hbc);
+ HBoxContainer *nav_hbc = memnew(HBoxContainer);
+ nav_hbc->add_theme_constant_override("separation", 0);
+ toolbar_hbc->add_child(nav_hbc);
+
button_hist_prev = memnew(Button);
button_hist_prev->set_flat(true);
button_hist_prev->set_disabled(true);
button_hist_prev->set_focus_mode(FOCUS_NONE);
button_hist_prev->set_tooltip_text(TTR("Go to previous selected folder/file."));
- toolbar_hbc->add_child(button_hist_prev);
+ nav_hbc->add_child(button_hist_prev);
button_hist_next = memnew(Button);
button_hist_next->set_flat(true);
button_hist_next->set_disabled(true);
button_hist_next->set_focus_mode(FOCUS_NONE);
button_hist_next->set_tooltip_text(TTR("Go to next selected folder/file."));
- toolbar_hbc->add_child(button_hist_next);
+ nav_hbc->add_child(button_hist_next);
current_path_line_edit = memnew(LineEdit);
current_path_line_edit->set_structured_text_bidi_override(TextServer::STRUCTURED_TEXT_FILE);
@@ -4180,13 +4144,12 @@ FileSystemDock::FileSystemDock() {
toolbar_hbc->add_child(button_toggle_display_mode);
button_dock_placement = memnew(Button);
- button_dock_placement->set_flat(true);
+ button_dock_placement->set_theme_type_variation("FlatMenuButton");
button_dock_placement->connect(SceneStringName(pressed), callable_mp(this, &FileSystemDock::_change_bottom_dock_placement));
button_dock_placement->hide();
toolbar_hbc->add_child(button_dock_placement);
toolbar2_hbc = memnew(HBoxContainer);
- toolbar2_hbc->add_theme_constant_override("separation", 0);
top_vbc->add_child(toolbar2_hbc);
tree_search_box = memnew(LineEdit);
@@ -4252,7 +4215,7 @@ FileSystemDock::FileSystemDock() {
path_hb->add_child(file_list_button_sort);
button_file_list_display_mode = memnew(Button);
- button_file_list_display_mode->set_flat(true);
+ button_file_list_display_mode->set_theme_type_variation("FlatMenuButton");
path_hb->add_child(button_file_list_display_mode);
files = memnew(FileSystemList);
diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h
index 108b646029..ee68f44d4c 100644
--- a/editor/filesystem_dock.h
+++ b/editor/filesystem_dock.h
@@ -33,6 +33,7 @@
#include "editor/dependency_editor.h"
#include "editor/editor_file_system.h"
+#include "editor/file_info.h"
#include "editor/plugins/script_editor_plugin.h"
#include "editor/script_create_dialog.h"
#include "scene/gui/box_container.h"
@@ -93,16 +94,6 @@ public:
DISPLAY_MODE_HSPLIT,
};
- enum FileSortOption {
- FILE_SORT_NAME = 0,
- FILE_SORT_NAME_REVERSE,
- FILE_SORT_TYPE,
- FILE_SORT_TYPE_REVERSE,
- FILE_SORT_MODIFIED_TIME,
- FILE_SORT_MODIFIED_TIME_REVERSE,
- FILE_SORT_MAX,
- };
-
enum Overwrite {
OVERWRITE_UNDECIDED,
OVERWRITE_REPLACE,
@@ -146,7 +137,7 @@ private:
HashMap<String, Color> folder_colors;
Dictionary assigned_folder_colors;
- FileSortOption file_sort = FILE_SORT_NAME;
+ FileSortOption file_sort = FileSortOption::FILE_SORT_NAME;
VBoxContainer *scanning_vb = nullptr;
ProgressBar *scanning_progress = nullptr;
@@ -336,25 +327,6 @@ private:
void _tree_empty_click(const Vector2 &p_pos, MouseButton p_button);
void _tree_empty_selected();
- struct FileInfo {
- String name;
- String path;
- String icon_path;
- StringName type;
- Vector<String> sources;
- bool import_broken = false;
- uint64_t modified_time = 0;
-
- bool operator<(const FileInfo &fi) const {
- return FileNoCaseComparator()(name, fi.name);
- }
- };
-
- struct FileInfoTypeComparator;
- struct FileInfoModifiedTimeComparator;
-
- void _sort_file_info_list(List<FileSystemDock::FileInfo> &r_file_list);
-
void _search(EditorFileSystemDirectory *p_path, List<FileInfo> *matches, int p_max_items);
void _set_current_path_line_edit_text(const String &p_path);
diff --git a/editor/gui/editor_dir_dialog.cpp b/editor/gui/editor_dir_dialog.cpp
index b677ba1098..23568a3c2a 100644
--- a/editor/gui/editor_dir_dialog.cpp
+++ b/editor/gui/editor_dir_dialog.cpp
@@ -215,8 +215,9 @@ EditorDirDialog::EditorDirDialog() {
makedir->connect(SceneStringName(pressed), callable_mp(this, &EditorDirDialog::_make_dir));
tree = memnew(Tree);
- vb->add_child(tree);
+ tree->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
tree->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ vb->add_child(tree);
tree->connect("item_activated", callable_mp(this, &EditorDirDialog::_item_activated));
tree->connect("item_collapsed", callable_mp(this, &EditorDirDialog::_item_collapsed), CONNECT_DEFERRED);
diff --git a/editor/gui/editor_file_dialog.cpp b/editor/gui/editor_file_dialog.cpp
index 18f1f6da0c..a63c3f7848 100644
--- a/editor/gui/editor_file_dialog.cpp
+++ b/editor/gui/editor_file_dialog.cpp
@@ -31,7 +31,6 @@
#include "editor_file_dialog.h"
#include "core/config/project_settings.h"
-#include "core/io/file_access.h"
#include "core/os/keyboard.h"
#include "core/os/os.h"
#include "editor/dependency_editor.h"
@@ -183,6 +182,9 @@ void EditorFileDialog::_update_theme_item_cache() {
theme_cache.favorites_down = get_editor_theme_icon(SNAME("MoveDown"));
theme_cache.create_folder = get_editor_theme_icon(SNAME("FolderCreate"));
+ theme_cache.filter_box = get_editor_theme_icon(SNAME("Search"));
+ theme_cache.file_sort_button = get_editor_theme_icon(SNAME("Sort"));
+
theme_cache.folder = get_editor_theme_icon(SNAME("Folder"));
theme_cache.folder_icon_color = get_theme_color(SNAME("folder_icon_color"), SNAME("FileDialog"));
@@ -326,6 +328,10 @@ void EditorFileDialog::shortcut_input(const Ref<InputEvent> &p_event) {
dir->select_all();
handled = true;
}
+ if (ED_IS_SHORTCUT("file_dialog/focus_filter", p_event)) {
+ _focus_filter_box();
+ handled = true;
+ }
if (ED_IS_SHORTCUT("file_dialog/move_favorite_up", p_event)) {
_favorite_move_up();
handled = true;
@@ -369,6 +375,8 @@ void EditorFileDialog::update_dir() {
}
dir->set_text(dir_access->get_current_dir(false));
+ filter_box->clear();
+
// Disable "Open" button only when selecting file(s) mode.
get_ok_button()->set_disabled(_is_open_should_be_disabled());
switch (mode) {
@@ -422,7 +430,13 @@ void EditorFileDialog::_post_popup() {
item_list->grab_focus();
}
- if (mode == FILE_MODE_OPEN_DIR) {
+ bool is_open_directory_mode = mode == FILE_MODE_OPEN_DIR;
+ PopupMenu *p = file_sort_button->get_popup();
+ p->set_item_disabled(2, is_open_directory_mode);
+ p->set_item_disabled(3, is_open_directory_mode);
+ p->set_item_disabled(4, is_open_directory_mode);
+ p->set_item_disabled(5, is_open_directory_mode);
+ if (is_open_directory_mode) {
file_box->set_visible(false);
} else {
file_box->set_visible(true);
@@ -956,7 +970,7 @@ void EditorFileDialog::update_file_list() {
dir_access->list_dir_begin();
- List<String> files;
+ List<FileInfo> file_infos;
List<String> dirs;
String item = dir_access->get_next();
@@ -967,27 +981,44 @@ void EditorFileDialog::update_file_list() {
continue;
}
- if (show_hidden_files) {
- if (!dir_access->current_is_dir()) {
- files.push_back(item);
- } else {
- dirs.push_back(item);
- }
- } else if (!dir_access->current_is_hidden()) {
- String full_path = cdir == "res://" ? item : dir_access->get_current_dir() + "/" + item;
- if (dir_access->current_is_dir()) {
- if (Engine::get_singleton()->is_project_manager_hint() || !EditorFileSystem::_should_skip_directory(full_path)) {
- dirs.push_back(item);
+ bool matches_search = true;
+ if (search_string.length() > 0) {
+ matches_search = item.find(search_string) != -1;
+ }
+
+ FileInfo file_info;
+ file_info.name = item;
+ file_info.path = cdir.path_join(file_info.name);
+ file_info.type = item.get_extension();
+ file_info.modified_time = FileAccess::get_modified_time(file_info.path);
+
+ if (matches_search) {
+ if (show_hidden_files) {
+ if (!dir_access->current_is_dir()) {
+ file_infos.push_back(file_info);
+ } else {
+ dirs.push_back(file_info.name);
+ }
+ } else if (!dir_access->current_is_hidden()) {
+ String full_path = cdir == "res://" ? file_info.name : dir_access->get_current_dir() + "/" + file_info.name;
+ if (dir_access->current_is_dir()) {
+ if (Engine::get_singleton()->is_project_manager_hint() || !EditorFileSystem::_should_skip_directory(full_path)) {
+ dirs.push_back(file_info.name);
+ }
+ } else {
+ file_infos.push_back(file_info);
}
- } else {
- files.push_back(item);
}
}
item = dir_access->get_next();
}
dirs.sort_custom<FileNoCaseComparator>();
- files.sort_custom<FileNoCaseComparator>();
+ bool reverse_directories = file_sort == FileSortOption::FILE_SORT_NAME_REVERSE;
+ if (reverse_directories) {
+ dirs.reverse();
+ }
+ sort_file_info_list(file_infos, file_sort);
while (!dirs.is_empty()) {
const String &dir_name = dirs.front()->get();
@@ -1037,25 +1068,26 @@ void EditorFileDialog::update_file_list() {
}
}
- while (!files.is_empty()) {
+ while (!file_infos.is_empty()) {
bool match = patterns.is_empty();
+ FileInfo file_info = file_infos.front()->get();
for (const String &E : patterns) {
- if (files.front()->get().matchn(E)) {
+ if (file_info.name.matchn(E)) {
match = true;
break;
}
}
if (match) {
- item_list->add_item(files.front()->get());
+ item_list->add_item(file_info.name);
if (get_icon_func) {
- Ref<Texture2D> icon = get_icon_func(cdir.path_join(files.front()->get()));
+ Ref<Texture2D> icon = get_icon_func(file_info.path);
if (display_mode == DISPLAY_THUMBNAILS) {
Ref<Texture2D> thumbnail;
if (get_thumbnail_func) {
- thumbnail = get_thumbnail_func(cdir.path_join(files.front()->get()));
+ thumbnail = get_thumbnail_func(file_info.path);
}
if (thumbnail.is_null()) {
thumbnail = file_thumbnail;
@@ -1069,22 +1101,21 @@ void EditorFileDialog::update_file_list() {
}
Dictionary d;
- d["name"] = files.front()->get();
+ d["name"] = file_info.name;
d["dir"] = false;
- String fullpath = cdir.path_join(files.front()->get());
- d["path"] = fullpath;
+ d["path"] = file_info.path;
item_list->set_item_metadata(-1, d);
if (display_mode == DISPLAY_THUMBNAILS && previews_enabled) {
- EditorResourcePreview::get_singleton()->queue_resource_preview(fullpath, this, "_thumbnail_result", fullpath);
+ EditorResourcePreview::get_singleton()->queue_resource_preview(file_info.path, this, "_thumbnail_result", file_info.path);
}
- if (file->get_text() == files.front()->get()) {
+ if (file->get_text() == file_info.name) {
item_list->set_current(item_list->get_item_count() - 1);
}
}
- files.pop_front();
+ file_infos.pop_front();
}
if (favorites->get_current() >= 0) {
@@ -1353,6 +1384,24 @@ void EditorFileDialog::_make_dir() {
makedirname->grab_focus();
}
+void EditorFileDialog::_focus_filter_box() {
+ filter_box->grab_focus();
+ filter_box->select_all();
+}
+
+void EditorFileDialog::_filter_changed(const String &p_text) {
+ search_string = p_text;
+ invalidate();
+}
+
+void EditorFileDialog::_file_sort_popup(int p_id) {
+ for (int i = 0; i != static_cast<int>(FileSortOption::FILE_SORT_MAX); i++) {
+ file_sort_button->get_popup()->set_item_checked(i, (i == p_id));
+ }
+ file_sort = static_cast<FileSortOption>(p_id);
+ invalidate();
+}
+
void EditorFileDialog::_delete_items() {
// Collect the selected folders and files to delete and check them in the deletion dependency dialog.
Vector<String> folders;
@@ -1444,6 +1493,10 @@ void EditorFileDialog::_update_icons() {
show_hidden->set_icon(theme_cache.toggle_hidden);
makedir->set_icon(theme_cache.create_folder);
+ filter_box->set_right_icon(theme_cache.filter_box);
+ file_sort_button->set_icon(theme_cache.file_sort_button);
+ filter_box->set_clear_button_enabled(true);
+
fav_up->set_icon(theme_cache.favorites_up);
fav_down->set_icon(theme_cache.favorites_down);
}
@@ -2093,6 +2146,7 @@ EditorFileDialog::EditorFileDialog() {
// Allow both Cmd + L and Cmd + Shift + G to match Safari's and Finder's shortcuts respectively.
ED_SHORTCUT_OVERRIDE_ARRAY("file_dialog/focus_path", "macos",
{ int32_t(KeyModifierMask::META | Key::L), int32_t(KeyModifierMask::META | KeyModifierMask::SHIFT | Key::G) });
+ ED_SHORTCUT("file_dialog/focus_filter", TTR("Focus Filter"), KeyModifierMask::CMD_OR_CTRL | Key::F);
ED_SHORTCUT("file_dialog/move_favorite_up", TTR("Move Favorite Up"), KeyModifierMask::CMD_OR_CTRL | Key::UP);
ED_SHORTCUT("file_dialog/move_favorite_down", TTR("Move Favorite Down"), KeyModifierMask::CMD_OR_CTRL | Key::DOWN);
@@ -2256,11 +2310,37 @@ EditorFileDialog::EditorFileDialog() {
VBoxContainer *list_vb = memnew(VBoxContainer);
list_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ HBoxContainer *lower_hb = memnew(HBoxContainer);
+ lower_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+
l = memnew(Label(TTR("Directories & Files:")));
l->set_theme_type_variation("HeaderSmall");
- list_vb->add_child(l);
+ lower_hb->add_child(l);
+
+ list_vb->add_child(lower_hb);
preview_hb->add_child(list_vb);
+ filter_box = memnew(LineEdit);
+ filter_box->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ filter_box->set_placeholder(TTR("Filter"));
+ filter_box->connect(SceneStringName(text_changed), callable_mp(this, &EditorFileDialog::_filter_changed));
+ lower_hb->add_child(filter_box);
+
+ file_sort_button = memnew(MenuButton);
+ file_sort_button->set_flat(true);
+ file_sort_button->set_tooltip_text(TTR("Sort files"));
+
+ PopupMenu *p = file_sort_button->get_popup();
+ p->connect(SceneStringName(id_pressed), callable_mp(this, &EditorFileDialog::_file_sort_popup));
+ p->add_radio_check_item(TTR("Sort by Name (Ascending)"), static_cast<int>(FileSortOption::FILE_SORT_NAME));
+ p->add_radio_check_item(TTR("Sort by Name (Descending)"), static_cast<int>(FileSortOption::FILE_SORT_NAME_REVERSE));
+ p->add_radio_check_item(TTR("Sort by Type (Ascending)"), static_cast<int>(FileSortOption::FILE_SORT_TYPE));
+ p->add_radio_check_item(TTR("Sort by Type (Descending)"), static_cast<int>(FileSortOption::FILE_SORT_TYPE_REVERSE));
+ p->add_radio_check_item(TTR("Sort by Last Modified"), static_cast<int>(FileSortOption::FILE_SORT_MODIFIED_TIME));
+ p->add_radio_check_item(TTR("Sort by First Modified"), static_cast<int>(FileSortOption::FILE_SORT_MODIFIED_TIME_REVERSE));
+ p->set_item_checked(0, true);
+ lower_hb->add_child(file_sort_button);
+
// Item (files and folders) list with context menu.
item_list = memnew(ItemList);
diff --git a/editor/gui/editor_file_dialog.h b/editor/gui/editor_file_dialog.h
index 6272e27f82..1922155133 100644
--- a/editor/gui/editor_file_dialog.h
+++ b/editor/gui/editor_file_dialog.h
@@ -32,13 +32,15 @@
#define EDITOR_FILE_DIALOG_H
#include "core/io/dir_access.h"
+#include "editor/file_info.h"
#include "scene/gui/dialogs.h"
#include "scene/property_list_helper.h"
-class GridContainer;
class DependencyRemoveDialog;
+class GridContainer;
class HSplitContainer;
class ItemList;
+class MenuButton;
class OptionButton;
class PopupMenu;
class TextureRect;
@@ -127,6 +129,11 @@ private:
Button *favorite = nullptr;
Button *show_hidden = nullptr;
+ String search_string;
+ LineEdit *filter_box = nullptr;
+ FileSortOption file_sort = FileSortOption::FILE_SORT_NAME;
+ MenuButton *file_sort_button = nullptr;
+
Button *fav_up = nullptr;
Button *fav_down = nullptr;
@@ -165,6 +172,9 @@ private:
Ref<Texture2D> favorites_up;
Ref<Texture2D> favorites_down;
+ Ref<Texture2D> filter_box;
+ Ref<Texture2D> file_sort_button;
+
Ref<Texture2D> folder;
Color folder_icon_color;
@@ -227,6 +237,10 @@ private:
void _make_dir();
void _make_dir_confirm();
+ void _focus_filter_box();
+ void _filter_changed(const String &p_text);
+ void _file_sort_popup(int p_id);
+
void _delete_items();
void _delete_files_global();
diff --git a/editor/icons/Marker.svg b/editor/icons/Marker.svg
new file mode 100644
index 0000000000..ff91a4a947
--- /dev/null
+++ b/editor/icons/Marker.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10" viewBox="-959.5 540.5 10 10"><path fill="#e0e0e0" d="m-954.5 550-3-3v-6h6v6z"/></svg> \ No newline at end of file
diff --git a/editor/icons/MarkerSelected.svg b/editor/icons/MarkerSelected.svg
new file mode 100644
index 0000000000..c581a3a651
--- /dev/null
+++ b/editor/icons/MarkerSelected.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10" viewBox="-959.5 540.5 10 10"><path fill="#5fb2ff" d="m-952 541.5v5.086l-2.5 2.5-2.5-2.5v-5.086zm1-1h-7v6.5l3.5 3.5 3.5-3.5z"/><path fill="#003e7a" d="m-957 546.586 2.5 2.5 2.5-2.5v-5.086h-5z"/></svg> \ No newline at end of file
diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp
index a28fe01666..9e282cb3fa 100644
--- a/editor/plugins/animation_blend_tree_editor_plugin.cpp
+++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp
@@ -36,6 +36,7 @@
#include "core/os/keyboard.h"
#include "editor/editor_inspector.h"
#include "editor/editor_node.h"
+#include "editor/editor_properties.h"
#include "editor/editor_settings.h"
#include "editor/editor_string_names.h"
#include "editor/editor_undo_redo_manager.h"
@@ -45,6 +46,7 @@
#include "scene/animation/animation_player.h"
#include "scene/gui/check_box.h"
#include "scene/gui/menu_button.h"
+#include "scene/gui/option_button.h"
#include "scene/gui/panel.h"
#include "scene/gui/progress_bar.h"
#include "scene/gui/separator.h"
@@ -1262,4 +1264,168 @@ AnimationNodeBlendTreeEditor::AnimationNodeBlendTreeEditor() {
open_file->set_title(TTR("Open Animation Node"));
open_file->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE);
open_file->connect("file_selected", callable_mp(this, &AnimationNodeBlendTreeEditor::_file_opened));
+
+ animation_node_inspector_plugin = Ref<EditorInspectorPluginAnimationNodeAnimation>(memnew(EditorInspectorPluginAnimationNodeAnimation));
+ EditorInspector::add_inspector_plugin(animation_node_inspector_plugin);
+}
+
+AnimationNodeBlendTreeEditor::~AnimationNodeBlendTreeEditor() {
+}
+
+// EditorPluginAnimationNodeAnimation
+
+void AnimationNodeAnimationEditor::_open_set_custom_timeline_from_marker_dialog() {
+ AnimationTree *tree = AnimationTreeEditor::get_singleton()->get_animation_tree();
+ StringName anim_name = animation_node_animation->get_animation();
+ PackedStringArray markers = tree->has_animation(anim_name) ? tree->get_animation(anim_name)->get_marker_names() : PackedStringArray();
+
+ dialog->select_start->clear();
+ dialog->select_start->add_icon_item(get_editor_theme_icon(SNAME("PlayStart")), TTR("Start of Animation"));
+ dialog->select_start->add_separator();
+ dialog->select_end->clear();
+ dialog->select_end->add_icon_item(get_editor_theme_icon(SNAME("PlayStartBackwards")), TTR("End of Animation"));
+ dialog->select_end->add_separator();
+
+ for (const String &marker : markers) {
+ dialog->select_start->add_item(marker);
+ dialog->select_end->add_item(marker);
+ }
+
+ // Because the default selections are always valid, and marker times won't change during the dialog, we can ensure that the user can only select valid markers.
+ // This invariant is maintained by _validate_markers.
+ dialog->select_start->select(0);
+ dialog->select_end->select(0);
+
+ dialog->popup_centered(Size2(200, 0) * EDSCALE);
+}
+
+void AnimationNodeAnimationEditor::_validate_markers(int p_id) {
+ // Note: p_id is ignored. It is included because OptionButton's item_changed signal always passes it.
+ int start_id = dialog->select_start->get_selected_id();
+ int end_id = dialog->select_end->get_selected_id();
+
+ StringName anim_name = animation_node_animation->get_animation();
+ Ref<Animation> animation = AnimationTreeEditor::get_singleton()->get_animation_tree()->get_animation(anim_name);
+ ERR_FAIL_COND(animation.is_null());
+
+ double start_time = start_id < 2 ? 0 : animation->get_marker_time(dialog->select_start->get_item_text(start_id));
+ double end_time = end_id < 2 ? animation->get_length() : animation->get_marker_time(dialog->select_end->get_item_text(end_id));
+
+ // p_start and p_end have the same item count.
+ for (int i = 2; i < dialog->select_start->get_item_count(); i++) {
+ String start_marker = dialog->select_start->get_item_text(i);
+ String end_marker = dialog->select_end->get_item_text(i);
+ dialog->select_start->set_item_disabled(i, end_id >= 2 && (i == end_id || animation->get_marker_time(start_marker) > end_time));
+ dialog->select_end->set_item_disabled(i, start_id >= 2 && (i == start_id || start_time > animation->get_marker_time(end_marker)));
+ }
+}
+
+void AnimationNodeAnimationEditor::_confirm_set_custom_timeline_from_marker_dialog() {
+ int start_id = dialog->select_start->get_selected_id();
+ int end_id = dialog->select_end->get_selected_id();
+
+ Ref<Animation> animation = AnimationTreeEditor::get_singleton()->get_animation_tree()->get_animation(animation_node_animation->get_animation());
+ ERR_FAIL_COND(animation.is_null());
+ double start_time = start_id < 2 ? 0 : animation->get_marker_time(dialog->select_start->get_item_text(start_id));
+ double end_time = end_id < 2 ? animation->get_length() : animation->get_marker_time(dialog->select_end->get_item_text(end_id));
+ double length = end_time - start_time;
+
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
+ undo_redo->create_action(TTR("Set Custom Timeline from Marker"));
+ undo_redo->add_do_method(*animation_node_animation, "set_start_offset", start_time);
+ undo_redo->add_undo_method(*animation_node_animation, "set_start_offset", animation_node_animation->get_start_offset());
+ undo_redo->add_do_method(*animation_node_animation, "set_stretch_time_scale", false);
+ undo_redo->add_undo_method(*animation_node_animation, "set_stretch_time_scale", animation_node_animation->is_stretching_time_scale());
+ undo_redo->add_do_method(*animation_node_animation, "set_timeline_length", length);
+ undo_redo->add_undo_method(*animation_node_animation, "set_timeline_length", animation_node_animation->get_timeline_length());
+ undo_redo->add_do_method(*animation_node_animation, "notify_property_list_changed");
+ undo_redo->add_undo_method(*animation_node_animation, "notify_property_list_changed");
+ undo_redo->commit_action();
+}
+
+AnimationNodeAnimationEditor::AnimationNodeAnimationEditor(Ref<AnimationNodeAnimation> p_animation_node_animation) {
+ animation_node_animation = p_animation_node_animation;
+
+ dialog = memnew(AnimationNodeAnimationEditorDialog);
+ add_child(dialog);
+ dialog->set_hide_on_ok(false);
+ dialog->select_start->connect(SceneStringName(item_selected), callable_mp(this, &AnimationNodeAnimationEditor::_validate_markers));
+ dialog->select_end->connect(SceneStringName(item_selected), callable_mp(this, &AnimationNodeAnimationEditor::_validate_markers));
+ dialog->connect(SceneStringName(confirmed), callable_mp(this, &AnimationNodeAnimationEditor::_confirm_set_custom_timeline_from_marker_dialog));
+
+ Control *top_spacer = memnew(Control);
+ add_child(top_spacer);
+ top_spacer->set_custom_minimum_size(Size2(0, 2) * EDSCALE);
+
+ button = memnew(Button);
+ add_child(button);
+ button->set_text(TTR("Set Custom Timeline from Marker"));
+ button->set_h_size_flags(Control::SIZE_SHRINK_CENTER);
+ button->connect(SceneStringName(pressed), callable_mp(this, &AnimationNodeAnimationEditor::_open_set_custom_timeline_from_marker_dialog));
+
+ Control *bottom_spacer = memnew(Control);
+ add_child(bottom_spacer);
+ bottom_spacer->set_custom_minimum_size(Size2(0, 2) * EDSCALE);
+}
+
+AnimationNodeAnimationEditor::~AnimationNodeAnimationEditor() {
+}
+
+void AnimationNodeAnimationEditor::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_THEME_CHANGED: {
+ button->set_theme_type_variation(SNAME("InspectorActionButton"));
+ button->set_icon(get_editor_theme_icon(SNAME("Edit")));
+ } break;
+ }
+}
+
+bool EditorInspectorPluginAnimationNodeAnimation::can_handle(Object *p_object) {
+ Ref<AnimationNodeAnimation> ana(Object::cast_to<AnimationNodeAnimation>(p_object));
+ return ana.is_valid() && ana->is_using_custom_timeline();
+}
+
+bool EditorInspectorPluginAnimationNodeAnimation::parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField<PropertyUsageFlags> p_usage, const bool p_wide) {
+ Ref<AnimationNodeAnimation> ana(Object::cast_to<AnimationNodeAnimation>(p_object));
+ ERR_FAIL_COND_V(ana.is_null(), false);
+
+ if (p_path == "timeline_length") {
+ add_custom_control(memnew(AnimationNodeAnimationEditor(ana)));
+ }
+
+ return false;
+}
+
+AnimationNodeAnimationEditorDialog::AnimationNodeAnimationEditorDialog() {
+ set_title(TTR("Select Markers..."));
+ VBoxContainer *vbox = memnew(VBoxContainer);
+ add_child(vbox);
+ vbox->set_offsets_preset(Control::PRESET_FULL_RECT);
+
+ HBoxContainer *container_start = memnew(HBoxContainer);
+ vbox->add_child(container_start);
+ Label *label_start = memnew(Label);
+ container_start->add_child(label_start);
+ label_start->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ label_start->set_stretch_ratio(1);
+ label_start->set_text(TTR("Start Marker"));
+ select_start = memnew(OptionButton);
+ container_start->add_child(select_start);
+ select_start->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ select_start->set_stretch_ratio(2);
+
+ HBoxContainer *container_end = memnew(HBoxContainer);
+ vbox->add_child(container_end);
+ Label *label_end = memnew(Label);
+ container_end->add_child(label_end);
+ label_end->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ label_end->set_stretch_ratio(1);
+ label_end->set_text(TTR("End Marker"));
+ select_end = memnew(OptionButton);
+ container_end->add_child(select_end);
+ select_end->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ select_end->set_stretch_ratio(2);
+}
+
+AnimationNodeAnimationEditorDialog::~AnimationNodeAnimationEditorDialog() {
}
diff --git a/editor/plugins/animation_blend_tree_editor_plugin.h b/editor/plugins/animation_blend_tree_editor_plugin.h
index ee6f087e07..9e7793977b 100644
--- a/editor/plugins/animation_blend_tree_editor_plugin.h
+++ b/editor/plugins/animation_blend_tree_editor_plugin.h
@@ -32,9 +32,11 @@
#define ANIMATION_BLEND_TREE_EDITOR_PLUGIN_H
#include "core/object/script_language.h"
+#include "editor/editor_inspector.h"
#include "editor/plugins/animation_tree_editor_plugin.h"
#include "scene/animation/animation_blend_tree.h"
#include "scene/gui/button.h"
+#include "scene/gui/dialogs.h"
#include "scene/gui/graph_edit.h"
#include "scene/gui/panel_container.h"
#include "scene/gui/popup.h"
@@ -47,6 +49,7 @@ class EditorFileDialog;
class EditorProperty;
class MenuButton;
class PanelContainer;
+class EditorInspectorPluginAnimationNodeAnimation;
class AnimationNodeBlendTreeEditor : public AnimationTreeNodeEditorPlugin {
GDCLASS(AnimationNodeBlendTreeEditor, AnimationTreeNodeEditorPlugin);
@@ -147,6 +150,8 @@ class AnimationNodeBlendTreeEditor : public AnimationTreeNodeEditorPlugin {
MENU_LOAD_FILE_CONFIRM = 1002
};
+ Ref<EditorInspectorPluginAnimationNodeAnimation> animation_node_inspector_plugin;
+
protected:
void _notification(int p_what);
static void _bind_methods();
@@ -165,6 +170,48 @@ public:
void update_graph();
AnimationNodeBlendTreeEditor();
+ ~AnimationNodeBlendTreeEditor();
+};
+
+// EditorPluginAnimationNodeAnimation
+
+class EditorInspectorPluginAnimationNodeAnimation : public EditorInspectorPlugin {
+ GDCLASS(EditorInspectorPluginAnimationNodeAnimation, EditorInspectorPlugin);
+
+public:
+ virtual bool can_handle(Object *p_object) override;
+ virtual bool parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField<PropertyUsageFlags> p_usage, const bool p_wide) override;
+};
+
+class AnimationNodeAnimationEditorDialog : public ConfirmationDialog {
+ GDCLASS(AnimationNodeAnimationEditorDialog, ConfirmationDialog);
+
+ friend class AnimationNodeAnimationEditor;
+
+ OptionButton *select_start = nullptr;
+ OptionButton *select_end = nullptr;
+
+public:
+ AnimationNodeAnimationEditorDialog();
+ ~AnimationNodeAnimationEditorDialog();
+};
+
+class AnimationNodeAnimationEditor : public VBoxContainer {
+ GDCLASS(AnimationNodeAnimationEditor, VBoxContainer);
+
+ Ref<AnimationNodeAnimation> animation_node_animation;
+ Button *button = nullptr;
+ AnimationNodeAnimationEditorDialog *dialog = nullptr;
+ void _open_set_custom_timeline_from_marker_dialog();
+ void _validate_markers(int p_id);
+ void _confirm_set_custom_timeline_from_marker_dialog();
+
+public:
+ AnimationNodeAnimationEditor(Ref<AnimationNodeAnimation> p_animation_node_animation);
+ ~AnimationNodeAnimationEditor();
+
+protected:
+ void _notification(int p_what);
};
#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 5cb558abbe..e6afc85e9e 100644
--- a/editor/plugins/animation_player_editor_plugin.cpp
+++ b/editor/plugins/animation_player_editor_plugin.cpp
@@ -41,6 +41,7 @@
#include "editor/editor_undo_redo_manager.h"
#include "editor/gui/editor_bottom_panel.h"
#include "editor/gui/editor_file_dialog.h"
+#include "editor/gui/editor_validation_panel.h"
#include "editor/inspector_dock.h"
#include "editor/plugins/canvas_item_editor_plugin.h" // For onion skinning.
#include "editor/plugins/node_3d_editor_plugin.h" // For onion skinning.
@@ -295,7 +296,14 @@ void AnimationPlayerEditor::_play_pressed() {
player->stop(); //so it won't blend with itself
}
ERR_FAIL_COND_EDMSG(!_validate_tracks(player->get_animation(current)), "Animation tracks may have any invalid key, abort playing.");
- player->play(current);
+ PackedStringArray markers = track_editor->get_selected_section();
+ if (markers.size() == 2) {
+ StringName start_marker = markers[0];
+ StringName end_marker = markers[1];
+ player->play_section_with_markers(current, start_marker, end_marker);
+ } else {
+ player->play(current);
+ }
}
//unstop
@@ -312,7 +320,14 @@ void AnimationPlayerEditor::_play_from_pressed() {
}
ERR_FAIL_COND_EDMSG(!_validate_tracks(player->get_animation(current)), "Animation tracks may have any invalid key, abort playing.");
player->seek_internal(time, true, true, true);
- player->play(current);
+ PackedStringArray markers = track_editor->get_selected_section();
+ if (markers.size() == 2) {
+ StringName start_marker = markers[0];
+ StringName end_marker = markers[1];
+ player->play_section_with_markers(current, start_marker, end_marker);
+ } else {
+ player->play(current);
+ }
}
//unstop
@@ -333,7 +348,14 @@ void AnimationPlayerEditor::_play_bw_pressed() {
player->stop(); //so it won't blend with itself
}
ERR_FAIL_COND_EDMSG(!_validate_tracks(player->get_animation(current)), "Animation tracks may have any invalid key, abort playing.");
- player->play_backwards(current);
+ PackedStringArray markers = track_editor->get_selected_section();
+ if (markers.size() == 2) {
+ StringName start_marker = markers[0];
+ StringName end_marker = markers[1];
+ player->play_section_with_markers_backwards(current, start_marker, end_marker);
+ } else {
+ player->play_backwards(current);
+ }
}
//unstop
@@ -350,7 +372,14 @@ void AnimationPlayerEditor::_play_bw_from_pressed() {
}
ERR_FAIL_COND_EDMSG(!_validate_tracks(player->get_animation(current)), "Animation tracks may have any invalid key, abort playing.");
player->seek_internal(time, true, true, true);
- player->play_backwards(current);
+ PackedStringArray markers = track_editor->get_selected_section();
+ if (markers.size() == 2) {
+ StringName start_marker = markers[0];
+ StringName end_marker = markers[1];
+ player->play_section_with_markers_backwards(current, start_marker, end_marker);
+ } else {
+ player->play_backwards(current);
+ }
}
//unstop
@@ -2397,3 +2426,24 @@ AnimationTrackKeyEditEditorPlugin::AnimationTrackKeyEditEditorPlugin() {
bool AnimationTrackKeyEditEditorPlugin::handles(Object *p_object) const {
return p_object->is_class("AnimationTrackKeyEdit");
}
+
+bool EditorInspectorPluginAnimationMarkerKeyEdit::can_handle(Object *p_object) {
+ return Object::cast_to<AnimationMarkerKeyEdit>(p_object) != nullptr;
+}
+
+void EditorInspectorPluginAnimationMarkerKeyEdit::parse_begin(Object *p_object) {
+ AnimationMarkerKeyEdit *amk = Object::cast_to<AnimationMarkerKeyEdit>(p_object);
+ ERR_FAIL_NULL(amk);
+
+ amk_editor = memnew(AnimationMarkerKeyEditEditor(amk->animation, amk->marker_name, amk->use_fps));
+ add_custom_control(amk_editor);
+}
+
+AnimationMarkerKeyEditEditorPlugin::AnimationMarkerKeyEditEditorPlugin() {
+ amk_plugin = memnew(EditorInspectorPluginAnimationMarkerKeyEdit);
+ EditorInspector::add_inspector_plugin(amk_plugin);
+}
+
+bool AnimationMarkerKeyEditEditorPlugin::handles(Object *p_object) const {
+ return p_object->is_class("AnimationMarkerKeyEdit");
+}
diff --git a/editor/plugins/animation_player_editor_plugin.h b/editor/plugins/animation_player_editor_plugin.h
index e4ca6c17c3..349ed7b5cd 100644
--- a/editor/plugins/animation_player_editor_plugin.h
+++ b/editor/plugins/animation_player_editor_plugin.h
@@ -338,4 +338,30 @@ public:
AnimationTrackKeyEditEditorPlugin();
};
+// AnimationMarkerKeyEditEditorPlugin
+
+class EditorInspectorPluginAnimationMarkerKeyEdit : public EditorInspectorPlugin {
+ GDCLASS(EditorInspectorPluginAnimationMarkerKeyEdit, EditorInspectorPlugin);
+
+ AnimationMarkerKeyEditEditor *amk_editor = nullptr;
+
+public:
+ virtual bool can_handle(Object *p_object) override;
+ virtual void parse_begin(Object *p_object) override;
+};
+
+class AnimationMarkerKeyEditEditorPlugin : public EditorPlugin {
+ GDCLASS(AnimationMarkerKeyEditEditorPlugin, EditorPlugin);
+
+ EditorInspectorPluginAnimationMarkerKeyEdit *amk_plugin = nullptr;
+
+public:
+ bool has_main_screen() const override { return false; }
+ virtual bool handles(Object *p_object) const override;
+
+ virtual String get_name() const override { return "AnimationMarkerKeyEdit"; }
+
+ AnimationMarkerKeyEditEditorPlugin();
+};
+
#endif // ANIMATION_PLAYER_EDITOR_PLUGIN_H
diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp
index af4b3a1643..fec8d4b897 100644
--- a/editor/plugins/asset_library_editor_plugin.cpp
+++ b/editor/plugins/asset_library_editor_plugin.cpp
@@ -993,7 +993,8 @@ void EditorAssetLibrary::_request_image(ObjectID p_for, int p_asset_id, String p
String url_host;
int url_port;
String url_path;
- Error err = trimmed_url.parse_url(url_scheme, url_host, url_port, url_path);
+ String url_fragment;
+ Error err = trimmed_url.parse_url(url_scheme, url_host, url_port, url_path, url_fragment);
if (err != OK) {
if (is_print_verbose_enabled()) {
ERR_PRINT(vformat("Asset Library: Invalid image URL '%s' for asset # %d.", trimmed_url, p_asset_id));
diff --git a/editor/plugins/tool_button_editor_plugin.cpp b/editor/plugins/tool_button_editor_plugin.cpp
new file mode 100644
index 0000000000..d9852c8694
--- /dev/null
+++ b/editor/plugins/tool_button_editor_plugin.cpp
@@ -0,0 +1,82 @@
+/**************************************************************************/
+/* tool_button_editor_plugin.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "tool_button_editor_plugin.h"
+
+#include "scene/gui/button.h"
+
+void EditorInspectorToolButtonPlugin::_update_action_icon(Button *p_action_button, const String &p_action_icon) {
+ p_action_button->set_icon(p_action_button->get_editor_theme_icon(p_action_icon));
+}
+
+void EditorInspectorToolButtonPlugin::_call_action(const Variant &p_object, const StringName &p_property) {
+ Object *object = p_object.get_validated_object();
+ ERR_FAIL_NULL_MSG(object, vformat(R"(Failed to get property "%s" on a previously freed instance.)", p_property));
+
+ const Variant value = object->get(p_property);
+ ERR_FAIL_COND_MSG(value.get_type() != Variant::CALLABLE, vformat(R"(The value of property "%s" is %s, but Callable was expected.)", p_property, Variant::get_type_name(value.get_type())));
+
+ const Callable callable = value;
+ ERR_FAIL_COND_MSG(!callable.is_valid(), vformat(R"(Tool button action "%s" is an invalid callable.)", callable));
+
+ Variant ret;
+ Callable::CallError ce;
+ callable.callp(nullptr, 0, ret, ce);
+ ERR_FAIL_COND_MSG(ce.error != Callable::CallError::CALL_OK, vformat(R"(Error calling tool button action "%s": %s)", callable, Variant::get_call_error_text(callable.get_method(), nullptr, 0, ce)));
+}
+
+bool EditorInspectorToolButtonPlugin::can_handle(Object *p_object) {
+ return true;
+}
+
+bool EditorInspectorToolButtonPlugin::parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField<PropertyUsageFlags> p_usage, const bool p_wide) {
+ if (p_type != Variant::CALLABLE || p_hint != PROPERTY_HINT_TOOL_BUTTON || !p_usage.has_flag(PROPERTY_USAGE_EDITOR)) {
+ return false;
+ }
+
+ const PackedStringArray splits = p_hint_text.rsplit(",", true, 1);
+ const String &hint_text = splits[0]; // Safe since `splits` cannot be empty.
+ const String &hint_icon = splits.size() > 1 ? splits[1] : "Callable";
+
+ Button *action_button = EditorInspector::create_inspector_action_button(hint_text);
+ action_button->set_auto_translate_mode(Node::AUTO_TRANSLATE_MODE_DISABLED);
+ action_button->set_disabled(p_usage & PROPERTY_USAGE_READ_ONLY);
+ action_button->connect(SceneStringName(theme_changed), callable_mp(this, &EditorInspectorToolButtonPlugin::_update_action_icon).bind(action_button, hint_icon));
+ action_button->connect(SceneStringName(pressed), callable_mp(this, &EditorInspectorToolButtonPlugin::_call_action).bind(p_object, p_path));
+
+ add_custom_control(action_button);
+ return true;
+}
+
+ToolButtonEditorPlugin::ToolButtonEditorPlugin() {
+ Ref<EditorInspectorToolButtonPlugin> plugin;
+ plugin.instantiate();
+ add_inspector_plugin(plugin);
+}
diff --git a/editor/plugins/tool_button_editor_plugin.h b/editor/plugins/tool_button_editor_plugin.h
new file mode 100644
index 0000000000..2d185c3a8f
--- /dev/null
+++ b/editor/plugins/tool_button_editor_plugin.h
@@ -0,0 +1,57 @@
+/**************************************************************************/
+/* tool_button_editor_plugin.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 TOOL_BUTTON_EDITOR_PLUGIN_H
+#define TOOL_BUTTON_EDITOR_PLUGIN_H
+
+#include "editor/editor_inspector.h"
+#include "editor/plugins/editor_plugin.h"
+
+class EditorInspectorToolButtonPlugin : public EditorInspectorPlugin {
+ GDCLASS(EditorInspectorToolButtonPlugin, EditorInspectorPlugin);
+
+ void _update_action_icon(Button *p_action_button, const String &p_action_icon);
+ void _call_action(const Variant &p_object, const StringName &p_property);
+
+public:
+ virtual bool can_handle(Object *p_object) override;
+ virtual bool parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField<PropertyUsageFlags> p_usage, const bool p_wide = false) override;
+};
+
+class ToolButtonEditorPlugin : public EditorPlugin {
+ GDCLASS(ToolButtonEditorPlugin, EditorPlugin);
+
+public:
+ virtual String get_name() const override { return "ToolButtonEditorPlugin"; }
+
+ ToolButtonEditorPlugin();
+};
+
+#endif // TOOL_BUTTON_EDITOR_PLUGIN_H
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index f98a30ebb3..ede8351e41 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -6300,6 +6300,7 @@ VisualShaderEditor::VisualShaderEditor() {
graph->connect("scroll_offset_changed", callable_mp(this, &VisualShaderEditor::_scroll_changed));
graph->connect("duplicate_nodes_request", callable_mp(this, &VisualShaderEditor::_duplicate_nodes));
graph->connect("copy_nodes_request", callable_mp(this, &VisualShaderEditor::_copy_nodes).bind(false));
+ graph->connect("cut_nodes_request", callable_mp(this, &VisualShaderEditor::_copy_nodes).bind(true));
graph->connect("paste_nodes_request", callable_mp(this, &VisualShaderEditor::_paste_nodes).bind(false, Point2()));
graph->connect("delete_nodes_request", callable_mp(this, &VisualShaderEditor::_delete_nodes_request));
graph->connect(SceneStringName(gui_input), callable_mp(this, &VisualShaderEditor::_graph_gui_input));
diff --git a/editor/project_manager/project_dialog.cpp b/editor/project_manager/project_dialog.cpp
index 7acda16890..01868846bf 100644
--- a/editor/project_manager/project_dialog.cpp
+++ b/editor/project_manager/project_dialog.cpp
@@ -1024,8 +1024,14 @@ ProjectDialog::ProjectDialog() {
add_child(fdialog_install);
project_name->connect(SceneStringName(text_changed), callable_mp(this, &ProjectDialog::_project_name_changed).unbind(1));
+ project_name->connect(SceneStringName(text_submitted), callable_mp(this, &ProjectDialog::ok_pressed).unbind(1));
+
project_path->connect(SceneStringName(text_changed), callable_mp(this, &ProjectDialog::_project_path_changed).unbind(1));
+ project_path->connect(SceneStringName(text_submitted), callable_mp(this, &ProjectDialog::ok_pressed).unbind(1));
+
install_path->connect(SceneStringName(text_changed), callable_mp(this, &ProjectDialog::_install_path_changed).unbind(1));
+ install_path->connect(SceneStringName(text_submitted), callable_mp(this, &ProjectDialog::ok_pressed).unbind(1));
+
fdialog_install->connect("dir_selected", callable_mp(this, &ProjectDialog::_install_path_selected));
fdialog_install->connect("file_selected", callable_mp(this, &ProjectDialog::_install_path_selected));
diff --git a/editor/register_editor_types.cpp b/editor/register_editor_types.cpp
index 00377a0dd2..19aeeb0612 100644
--- a/editor/register_editor_types.cpp
+++ b/editor/register_editor_types.cpp
@@ -127,6 +127,7 @@
#include "editor/plugins/texture_region_editor_plugin.h"
#include "editor/plugins/theme_editor_plugin.h"
#include "editor/plugins/tiles/tiles_editor_plugin.h"
+#include "editor/plugins/tool_button_editor_plugin.h"
#include "editor/plugins/version_control_editor_plugin.h"
#include "editor/plugins/visual_shader_editor_plugin.h"
#include "editor/plugins/voxel_gi_editor_plugin.h"
@@ -247,6 +248,7 @@ void register_editor_types() {
EditorPlugins::add_by_type<TextureLayeredEditorPlugin>();
EditorPlugins::add_by_type<TextureRegionEditorPlugin>();
EditorPlugins::add_by_type<ThemeEditorPlugin>();
+ EditorPlugins::add_by_type<ToolButtonEditorPlugin>();
EditorPlugins::add_by_type<VoxelGIEditorPlugin>();
// 2D
diff --git a/editor/renames_map_3_to_4.cpp b/editor/renames_map_3_to_4.cpp
index ae7c86a5e5..ca5d3cbf2a 100644
--- a/editor/renames_map_3_to_4.cpp
+++ b/editor/renames_map_3_to_4.cpp
@@ -1354,7 +1354,6 @@ const char *RenamesMap3To4::project_settings_renames[][2] = {
{ "rendering/quality/shading/force_lambert_over_burley", "rendering/shading/overrides/force_lambert_over_burley" },
{ "rendering/quality/shading/force_lambert_over_burley.mobile", "rendering/shading/overrides/force_lambert_over_burley.mobile" },
{ "rendering/quality/shading/force_vertex_shading", "rendering/shading/overrides/force_vertex_shading" },
- { "rendering/quality/shading/force_vertex_shading.mobile", "rendering/shading/overrides/force_vertex_shading.mobile" },
{ "rendering/quality/shadow_atlas/quadrant_0_subdiv", "rendering/lights_and_shadows/shadow_atlas/quadrant_0_subdiv" },
{ "rendering/quality/shadow_atlas/quadrant_1_subdiv", "rendering/lights_and_shadows/shadow_atlas/quadrant_1_subdiv" },
{ "rendering/quality/shadow_atlas/quadrant_2_subdiv", "rendering/lights_and_shadows/shadow_atlas/quadrant_2_subdiv" },
@@ -1400,7 +1399,6 @@ const char *RenamesMap3To4::project_godot_renames[][2] = {
{ "quality/shading/force_lambert_over_burley", "shading/overrides/force_lambert_over_burley" },
{ "quality/shading/force_lambert_over_burley.mobile", "shading/overrides/force_lambert_over_burley.mobile" },
{ "quality/shading/force_vertex_shading", "shading/overrides/force_vertex_shading" },
- { "quality/shading/force_vertex_shading.mobile", "shading/overrides/force_vertex_shading.mobile" },
{ "quality/shadow_atlas/quadrant_0_subdiv", "lights_and_shadows/shadow_atlas/quadrant_0_subdiv" },
{ "quality/shadow_atlas/quadrant_1_subdiv", "lights_and_shadows/shadow_atlas/quadrant_1_subdiv" },
{ "quality/shadow_atlas/quadrant_2_subdiv", "lights_and_shadows/shadow_atlas/quadrant_2_subdiv" },
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index 7187da851e..ad84846a6d 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -436,6 +436,22 @@ void SceneTreeDock::_replace_with_branch_scene(const String &p_file, Node *base)
instantiated_scene->set_unique_name_in_owner(base->is_unique_name_in_owner());
+ Node2D *copy_2d = Object::cast_to<Node2D>(instantiated_scene);
+ Node2D *base_2d = Object::cast_to<Node2D>(base);
+ if (copy_2d && base_2d) {
+ copy_2d->set_position(base_2d->get_position());
+ copy_2d->set_rotation(base_2d->get_rotation());
+ copy_2d->set_scale(base_2d->get_scale());
+ }
+
+ Node3D *copy_3d = Object::cast_to<Node3D>(instantiated_scene);
+ Node3D *base_3d = Object::cast_to<Node3D>(base);
+ if (copy_3d && base_3d) {
+ copy_3d->set_position(base_3d->get_position());
+ copy_3d->set_rotation(base_3d->get_rotation());
+ copy_3d->set_scale(base_3d->get_scale());
+ }
+
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("Replace with Branch Scene"));
@@ -3259,6 +3275,36 @@ void SceneTreeDock::_new_scene_from(const String &p_file) {
// Root node cannot ever be unique name in its own Scene!
copy->set_unique_name_in_owner(false);
+ const Dictionary dict = new_scene_from_dialog->get_selected_options();
+ bool reset_position = dict.get(TTR("Reset Position"), true);
+ bool reset_scale = dict.get(TTR("Reset Scale"), false);
+ bool reset_rotation = dict.get(TTR("Reset Rotation"), false);
+
+ Node2D *copy_2d = Object::cast_to<Node2D>(copy);
+ if (copy_2d != nullptr) {
+ if (reset_position) {
+ copy_2d->set_position(Vector2(0, 0));
+ }
+ if (reset_rotation) {
+ copy_2d->set_rotation(0);
+ }
+ if (reset_scale) {
+ copy_2d->set_scale(Size2(1, 1));
+ }
+ }
+ Node3D *copy_3d = Object::cast_to<Node3D>(copy);
+ if (copy_3d != nullptr) {
+ if (reset_position) {
+ copy_3d->set_position(Vector3(0, 0, 0));
+ }
+ if (reset_rotation) {
+ copy_3d->set_rotation(Vector3(0, 0, 0));
+ }
+ if (reset_scale) {
+ copy_3d->set_scale(Vector3(0, 0, 0));
+ }
+ }
+
Ref<PackedScene> sdata = memnew(PackedScene);
Error err = sdata->pack(copy);
memdelete(copy);
@@ -4668,6 +4714,9 @@ SceneTreeDock::SceneTreeDock(Node *p_scene_root, EditorSelection *p_editor_selec
new_scene_from_dialog = memnew(EditorFileDialog);
new_scene_from_dialog->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);
+ new_scene_from_dialog->add_option(TTR("Reset Position"), Vector<String>(), true);
+ new_scene_from_dialog->add_option(TTR("Reset Rotation"), Vector<String>(), false);
+ new_scene_from_dialog->add_option(TTR("Reset Scale"), Vector<String>(), false);
add_child(new_scene_from_dialog);
new_scene_from_dialog->connect("file_selected", callable_mp(this, &SceneTreeDock::_new_scene_from));
diff --git a/methods.py b/methods.py
index 3f11d39bd0..04541c72ad 100644
--- a/methods.py
+++ b/methods.py
@@ -795,9 +795,9 @@ def get_compiler_version(env):
"major": -1,
"minor": -1,
"patch": -1,
- "metadata1": None,
- "metadata2": None,
- "date": None,
+ "metadata1": "",
+ "metadata2": "",
+ "date": "",
"apple_major": -1,
"apple_minor": -1,
"apple_patch1": -1,
@@ -806,7 +806,35 @@ def get_compiler_version(env):
}
if env.msvc and not using_clang(env):
- # TODO: Implement for MSVC
+ try:
+ # FIXME: `-latest` works for most cases, but there are edge-cases where this would
+ # benefit from a more nuanced search.
+ # https://github.com/godotengine/godot/pull/91069#issuecomment-2358956731
+ # https://github.com/godotengine/godot/pull/91069#issuecomment-2380836341
+ args = [
+ env["VSWHERE"],
+ "-latest",
+ "-prerelease",
+ "-products",
+ "*",
+ "-requires",
+ "Microsoft.Component.MSBuild",
+ "-utf8",
+ ]
+ version = subprocess.check_output(args, encoding="utf-8").strip()
+ for line in version.splitlines():
+ split = line.split(":", 1)
+ if split[0] == "catalog_productDisplayVersion":
+ sem_ver = split[1].split(".")
+ ret["major"] = int(sem_ver[0])
+ ret["minor"] = int(sem_ver[1])
+ ret["patch"] = int(sem_ver[2])
+ # Could potentially add section for determining preview version, but
+ # that can wait until metadata is actually used for something.
+ if split[0] == "catalog_buildVersion":
+ ret["metadata1"] = split[1]
+ except (subprocess.CalledProcessError, OSError):
+ print_warning("Couldn't find vswhere to determine compiler version.")
return ret
# Not using -dumpversion as some GCC distros only return major, and
diff --git a/modules/bcdec/SCsub b/modules/bcdec/SCsub
new file mode 100644
index 0000000000..32198eff96
--- /dev/null
+++ b/modules/bcdec/SCsub
@@ -0,0 +1,10 @@
+#!/usr/bin/env python
+from misc.utility.scons_hints import *
+
+Import("env")
+Import("env_modules")
+
+env_bcdec = env_modules.Clone()
+
+# Godot source files
+env_bcdec.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/squish/config.py b/modules/bcdec/config.py
index d22f9454ed..d22f9454ed 100644
--- a/modules/squish/config.py
+++ b/modules/bcdec/config.py
diff --git a/modules/bcdec/image_decompress_bcdec.cpp b/modules/bcdec/image_decompress_bcdec.cpp
new file mode 100644
index 0000000000..30ca1fccb3
--- /dev/null
+++ b/modules/bcdec/image_decompress_bcdec.cpp
@@ -0,0 +1,181 @@
+/**************************************************************************/
+/* image_decompress_bcdec.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "image_decompress_bcdec.h"
+
+#include "core/os/os.h"
+#include "core/string/print_string.h"
+
+#define BCDEC_IMPLEMENTATION
+#include "thirdparty/misc/bcdec.h"
+
+inline void bcdec_bc6h_half_s(const void *compressedBlock, void *decompressedBlock, int destinationPitch) {
+ bcdec_bc6h_half(compressedBlock, decompressedBlock, destinationPitch, true);
+}
+
+inline void bcdec_bc6h_half_u(const void *compressedBlock, void *decompressedBlock, int destinationPitch) {
+ bcdec_bc6h_half(compressedBlock, decompressedBlock, destinationPitch, false);
+}
+
+static void decompress_image(BCdecFormat format, const void *src, void *dst, const uint64_t width, const uint64_t height) {
+ const uint8_t *src_blocks = reinterpret_cast<const uint8_t *>(src);
+ uint8_t *dec_blocks = reinterpret_cast<uint8_t *>(dst);
+ uint64_t src_pos = 0, dst_pos = 0;
+
+#define DECOMPRESS_LOOP(func, block_size, color_bytesize, color_components) \
+ for (uint64_t y = 0; y < height; y += 4) { \
+ for (uint64_t x = 0; x < width; x += 4) { \
+ func(&src_blocks[src_pos], &dec_blocks[dst_pos], width *color_components); \
+ src_pos += block_size; \
+ dst_pos += 4 * color_bytesize; \
+ } \
+ dst_pos += 3 * width * color_bytesize; \
+ }
+
+ switch (format) {
+ case BCdec_BC1: {
+ DECOMPRESS_LOOP(bcdec_bc1, BCDEC_BC1_BLOCK_SIZE, 4, 4)
+ } break;
+ case BCdec_BC2: {
+ DECOMPRESS_LOOP(bcdec_bc2, BCDEC_BC2_BLOCK_SIZE, 4, 4)
+ } break;
+ case BCdec_BC3: {
+ DECOMPRESS_LOOP(bcdec_bc3, BCDEC_BC3_BLOCK_SIZE, 4, 4)
+ } break;
+ case BCdec_BC4: {
+ DECOMPRESS_LOOP(bcdec_bc4, BCDEC_BC4_BLOCK_SIZE, 1, 1)
+ } break;
+ case BCdec_BC5: {
+ DECOMPRESS_LOOP(bcdec_bc5, BCDEC_BC5_BLOCK_SIZE, 2, 2)
+ } break;
+ case BCdec_BC6U: {
+ DECOMPRESS_LOOP(bcdec_bc6h_half_u, BCDEC_BC6H_BLOCK_SIZE, 6, 3)
+ } break;
+ case BCdec_BC6S: {
+ DECOMPRESS_LOOP(bcdec_bc6h_half_s, BCDEC_BC6H_BLOCK_SIZE, 6, 3)
+ } break;
+ case BCdec_BC7: {
+ DECOMPRESS_LOOP(bcdec_bc7, BCDEC_BC7_BLOCK_SIZE, 4, 4)
+ } break;
+ }
+
+#undef DECOMPRESS_LOOP
+}
+
+void image_decompress_bcdec(Image *p_image) {
+ uint64_t start_time = OS::get_singleton()->get_ticks_msec();
+
+ int w = p_image->get_width();
+ int h = p_image->get_height();
+
+ Image::Format source_format = p_image->get_format();
+ Image::Format target_format = Image::FORMAT_MAX;
+
+ BCdecFormat bcdec_format = BCdec_BC1;
+
+ switch (source_format) {
+ case Image::FORMAT_DXT1:
+ bcdec_format = BCdec_BC1;
+ target_format = Image::FORMAT_RGBA8;
+ break;
+
+ case Image::FORMAT_DXT3:
+ bcdec_format = BCdec_BC2;
+ target_format = Image::FORMAT_RGBA8;
+ break;
+
+ case Image::FORMAT_DXT5:
+ case Image::FORMAT_DXT5_RA_AS_RG:
+ bcdec_format = BCdec_BC3;
+ target_format = Image::FORMAT_RGBA8;
+ break;
+
+ case Image::FORMAT_RGTC_R:
+ bcdec_format = BCdec_BC4;
+ target_format = Image::FORMAT_R8;
+ break;
+
+ case Image::FORMAT_RGTC_RG:
+ bcdec_format = BCdec_BC5;
+ target_format = Image::FORMAT_RG8;
+ break;
+
+ case Image::FORMAT_BPTC_RGBFU:
+ bcdec_format = BCdec_BC6U;
+ target_format = Image::FORMAT_RGBH;
+ break;
+
+ case Image::FORMAT_BPTC_RGBF:
+ bcdec_format = BCdec_BC6S;
+ target_format = Image::FORMAT_RGBH;
+ break;
+
+ case Image::FORMAT_BPTC_RGBA:
+ bcdec_format = BCdec_BC7;
+ target_format = Image::FORMAT_RGBA8;
+ break;
+
+ default:
+ ERR_FAIL_MSG("bcdec: Can't decompress unknown format: " + Image::get_format_name(source_format) + ".");
+ break;
+ }
+
+ int mm_count = p_image->get_mipmap_count();
+ int64_t target_size = Image::get_image_data_size(w, h, target_format, p_image->has_mipmaps());
+
+ Vector<uint8_t> data;
+ data.resize(target_size);
+
+ const uint8_t *rb = p_image->get_data().ptr();
+ uint8_t *wb = data.ptrw();
+
+ // Decompress mipmaps.
+ for (int i = 0; i <= mm_count; i++) {
+ int64_t src_ofs = 0, mipmap_size = 0;
+ int mipmap_w = 0, mipmap_h = 0;
+ p_image->get_mipmap_offset_size_and_dimensions(i, src_ofs, mipmap_size, mipmap_w, mipmap_h);
+
+ int64_t dst_ofs = Image::get_image_mipmap_offset(p_image->get_width(), p_image->get_height(), target_format, i);
+ decompress_image(bcdec_format, rb + src_ofs, wb + dst_ofs, mipmap_w, mipmap_h);
+
+ w >>= 1;
+ h >>= 1;
+ }
+
+ p_image->set_data(p_image->get_width(), p_image->get_height(), p_image->has_mipmaps(), target_format, data);
+
+ // Swap channels if necessary.
+ if (source_format == Image::FORMAT_DXT5_RA_AS_RG) {
+ p_image->convert_ra_rgba8_to_rg();
+ }
+
+ print_verbose(vformat("bcdec: Decompression of a %dx%d %s image with %d mipmaps took %d ms.",
+ p_image->get_width(), p_image->get_height(), Image::get_format_name(source_format), p_image->get_mipmap_count(), OS::get_singleton()->get_ticks_msec() - start_time));
+}
diff --git a/modules/squish/image_decompress_squish.h b/modules/bcdec/image_decompress_bcdec.h
index 53c5d344a5..b82ceed9a4 100644
--- a/modules/squish/image_decompress_squish.h
+++ b/modules/bcdec/image_decompress_bcdec.h
@@ -1,5 +1,5 @@
/**************************************************************************/
-/* image_decompress_squish.h */
+/* image_decompress_bcdec.h */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,11 +28,22 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
-#ifndef IMAGE_DECOMPRESS_SQUISH_H
-#define IMAGE_DECOMPRESS_SQUISH_H
+#ifndef IMAGE_DECOMPRESS_BCDEC_H
+#define IMAGE_DECOMPRESS_BCDEC_H
#include "core/io/image.h"
-void image_decompress_squish(Image *p_image);
+enum BCdecFormat {
+ BCdec_BC1,
+ BCdec_BC2,
+ BCdec_BC3,
+ BCdec_BC4,
+ BCdec_BC5,
+ BCdec_BC6S,
+ BCdec_BC6U,
+ BCdec_BC7,
+};
-#endif // IMAGE_DECOMPRESS_SQUISH_H
+void image_decompress_bcdec(Image *p_image);
+
+#endif // IMAGE_DECOMPRESS_BCDEC_H
diff --git a/modules/squish/register_types.cpp b/modules/bcdec/register_types.cpp
index af7cf8f4f1..cbf9c0d383 100644
--- a/modules/squish/register_types.cpp
+++ b/modules/bcdec/register_types.cpp
@@ -30,17 +30,18 @@
#include "register_types.h"
-#include "image_decompress_squish.h"
+#include "image_decompress_bcdec.h"
-void initialize_squish_module(ModuleInitializationLevel p_level) {
+void initialize_bcdec_module(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
return;
}
- Image::_image_decompress_bc = image_decompress_squish;
+ Image::_image_decompress_bc = image_decompress_bcdec;
+ Image::_image_decompress_bptc = image_decompress_bcdec;
}
-void uninitialize_squish_module(ModuleInitializationLevel p_level) {
+void uninitialize_bcdec_module(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
return;
}
diff --git a/modules/squish/register_types.h b/modules/bcdec/register_types.h
index 1786b28ed3..eb721e3f2a 100644
--- a/modules/squish/register_types.h
+++ b/modules/bcdec/register_types.h
@@ -28,12 +28,12 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
-#ifndef SQUISH_REGISTER_TYPES_H
-#define SQUISH_REGISTER_TYPES_H
+#ifndef BCDEC_REGISTER_TYPES_H
+#define BCDEC_REGISTER_TYPES_H
#include "modules/register_module_types.h"
-void initialize_squish_module(ModuleInitializationLevel p_level);
-void uninitialize_squish_module(ModuleInitializationLevel p_level);
+void initialize_bcdec_module(ModuleInitializationLevel p_level);
+void uninitialize_bcdec_module(ModuleInitializationLevel p_level);
-#endif // SQUISH_REGISTER_TYPES_H
+#endif // BCDEC_REGISTER_TYPES_H
diff --git a/modules/camera/buffer_decoder.cpp b/modules/camera/buffer_decoder.cpp
index 024a68f080..85cfea242c 100644
--- a/modules/camera/buffer_decoder.cpp
+++ b/modules/camera/buffer_decoder.cpp
@@ -105,7 +105,7 @@ void SeparateYuyvBufferDecoder::decode(StreamingBuffer p_buffer) {
cbcr_image.instantiate(width, height, false, Image::FORMAT_RGB8, cbcr_image_data);
}
- camera_feed->set_YCbCr_imgs(y_image, cbcr_image);
+ camera_feed->set_ycbcr_images(y_image, cbcr_image);
}
YuyvToGrayscaleBufferDecoder::YuyvToGrayscaleBufferDecoder(CameraFeed *p_camera_feed) :
@@ -133,7 +133,7 @@ void YuyvToGrayscaleBufferDecoder::decode(StreamingBuffer p_buffer) {
image.instantiate(width, height, false, Image::FORMAT_RGB8, image_data);
}
- camera_feed->set_RGB_img(image);
+ camera_feed->set_rgb_image(image);
}
YuyvToRgbBufferDecoder::YuyvToRgbBufferDecoder(CameraFeed *p_camera_feed) :
@@ -176,7 +176,7 @@ void YuyvToRgbBufferDecoder::decode(StreamingBuffer p_buffer) {
image.instantiate(width, height, false, Image::FORMAT_RGB8, image_data);
}
- camera_feed->set_RGB_img(image);
+ camera_feed->set_rgb_image(image);
}
CopyBufferDecoder::CopyBufferDecoder(CameraFeed *p_camera_feed, bool p_rgba) :
@@ -195,7 +195,7 @@ void CopyBufferDecoder::decode(StreamingBuffer p_buffer) {
image.instantiate(width, height, false, rgba ? Image::FORMAT_RGBA8 : Image::FORMAT_LA8, image_data);
}
- camera_feed->set_RGB_img(image);
+ camera_feed->set_rgb_image(image);
}
JpegBufferDecoder::JpegBufferDecoder(CameraFeed *p_camera_feed) :
@@ -207,6 +207,6 @@ void JpegBufferDecoder::decode(StreamingBuffer p_buffer) {
uint8_t *dst = (uint8_t *)image_data.ptrw();
memcpy(dst, p_buffer.start, p_buffer.length);
if (image->load_jpg_from_buffer(image_data) == OK) {
- camera_feed->set_RGB_img(image);
+ camera_feed->set_rgb_image(image);
}
}
diff --git a/modules/camera/camera_feed_linux.cpp b/modules/camera/camera_feed_linux.cpp
index 9ed8eb0d0a..94bb2b6ad3 100644
--- a/modules/camera/camera_feed_linux.cpp
+++ b/modules/camera/camera_feed_linux.cpp
@@ -232,6 +232,7 @@ String CameraFeedLinux::get_device_name() const {
}
bool CameraFeedLinux::activate_feed() {
+ ERR_FAIL_COND_V_MSG(selected_format == -1, false, "CameraFeed format needs to be set before activating.");
file_descriptor = open(device_name.ascii(), O_RDWR | O_NONBLOCK, 0);
if (_request_buffers() && _start_capturing()) {
buffer_decoder = _create_buffer_decoder();
@@ -302,16 +303,14 @@ Array CameraFeedLinux::get_formats() const {
}
CameraFeed::FeedFormat CameraFeedLinux::get_format() const {
- return formats[selected_format];
+ FeedFormat feed_format = {};
+ return selected_format == -1 ? feed_format : formats[selected_format];
}
bool CameraFeedLinux::set_format(int p_index, const Dictionary &p_parameters) {
ERR_FAIL_COND_V_MSG(active, false, "Feed is active.");
ERR_FAIL_INDEX_V_MSG(p_index, formats.size(), false, "Invalid format index.");
- parameters = p_parameters.duplicate();
- selected_format = p_index;
-
FeedFormat feed_format = formats[p_index];
file_descriptor = open(device_name.ascii(), O_RDWR | O_NONBLOCK, 0);
@@ -344,6 +343,8 @@ bool CameraFeedLinux::set_format(int p_index, const Dictionary &p_parameters) {
}
close(file_descriptor);
+ parameters = p_parameters.duplicate();
+ selected_format = p_index;
emit_signal(SNAME("format_changed"));
return true;
@@ -353,7 +354,6 @@ CameraFeedLinux::CameraFeedLinux(const String &p_device_name) :
CameraFeed() {
device_name = p_device_name;
_query_device(device_name);
- set_format(0, Dictionary());
}
CameraFeedLinux::~CameraFeedLinux() {
diff --git a/modules/camera/camera_macos.mm b/modules/camera/camera_macos.mm
index 578a1d6325..de4f814846 100644
--- a/modules/camera/camera_macos.mm
+++ b/modules/camera/camera_macos.mm
@@ -182,7 +182,7 @@
}
// set our texture...
- feed->set_YCbCr_imgs(img[0], img[1]);
+ feed->set_ycbcr_images(img[0], img[1]);
}
// and unlock
diff --git a/modules/cvtt/register_types.cpp b/modules/cvtt/register_types.cpp
index 211d419349..80e3062d04 100644
--- a/modules/cvtt/register_types.cpp
+++ b/modules/cvtt/register_types.cpp
@@ -40,7 +40,6 @@ void initialize_cvtt_module(ModuleInitializationLevel p_level) {
}
Image::set_compress_bptc_func(image_compress_cvtt);
- Image::_image_decompress_bptc = image_decompress_cvtt;
}
void uninitialize_cvtt_module(ModuleInitializationLevel p_level) {
diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml
index f539f27848..5fe47d69df 100644
--- a/modules/gdscript/doc_classes/@GDScript.xml
+++ b/modules/gdscript/doc_classes/@GDScript.xml
@@ -669,6 +669,41 @@
[b]Note:[/b] Subgroups cannot be nested, they only provide one extra level of depth. Just like the next group ends the previous group, so do the subsequent subgroups.
</description>
</annotation>
+ <annotation name="@export_tool_button">
+ <return type="void" />
+ <param index="0" name="text" type="String" />
+ <param index="1" name="icon" type="String" default="&quot;&quot;" />
+ <description>
+ Export a [Callable] property as a clickable button with the label [param text]. When the button is pressed, the callable is called.
+ If [param icon] is specified, it is used to fetch an icon for the button via [method Control.get_theme_icon], from the [code]"EditorIcons"[/code] theme type. If [param icon] is omitted, the default [code]"Callable"[/code] icon is used instead.
+ Consider using the [EditorUndoRedoManager] to allow the action to be reverted safely.
+ See also [constant PROPERTY_HINT_TOOL_BUTTON].
+ [codeblock]
+ @tool
+ extends Sprite2D
+
+ @export_tool_button("Hello") var hello_action = hello
+ @export_tool_button("Randomize the color!", "ColorRect")
+ var randomize_color_action = randomize_color
+
+ func hello():
+ print("Hello world!")
+
+ func randomize_color():
+ var undo_redo = EditorInterface.get_editor_undo_redo()
+ undo_redo.create_action("Randomized Sprite2D Color")
+ undo_redo.add_do_property(self, &amp;"self_modulate", Color(randf(), randf(), randf()))
+ undo_redo.add_undo_property(self, &amp;"self_modulate", self_modulate)
+ undo_redo.commit_action()
+ [/codeblock]
+ [b]Note:[/b] The property is exported without the [constant PROPERTY_USAGE_STORAGE] flag because a [Callable] cannot be properly serialized and stored in a file.
+ [b]Note:[/b] In an exported project neither [EditorInterface] nor [EditorUndoRedoManager] exist, which may cause some scripts to break. To prevent this, you can use [method Engine.get_singleton] and omit the static type from the variable declaration:
+ [codeblock]
+ var undo_redo = Engine.get_singleton(&amp;"EditorInterface").get_editor_undo_redo()
+ [/codeblock]
+ [b]Note:[/b] Avoid storing lambda callables in member variables of [RefCounted]-based classes (e.g. resources), as this can lead to memory leaks. Use only method callables and optionally [method Callable.bind] or [method Callable.unbind].
+ </description>
+ </annotation>
<annotation name="@icon">
<return type="void" />
<param index="0" name="icon_path" type="String" />
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index 65aa150be3..e169566705 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -122,6 +122,7 @@ GDScriptParser::GDScriptParser() {
register_annotation(MethodInfo("@export_flags_avoidance"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_AVOIDANCE, Variant::INT>);
register_annotation(MethodInfo("@export_storage"), AnnotationInfo::VARIABLE, &GDScriptParser::export_storage_annotation);
register_annotation(MethodInfo("@export_custom", PropertyInfo(Variant::INT, "hint", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_CLASS_IS_ENUM, "PropertyHint"), PropertyInfo(Variant::STRING, "hint_string"), PropertyInfo(Variant::INT, "usage", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_CLASS_IS_BITFIELD, "PropertyUsageFlags")), AnnotationInfo::VARIABLE, &GDScriptParser::export_custom_annotation, varray(PROPERTY_USAGE_DEFAULT));
+ register_annotation(MethodInfo("@export_tool_button", PropertyInfo(Variant::STRING, "text"), PropertyInfo(Variant::STRING, "icon")), AnnotationInfo::VARIABLE, &GDScriptParser::export_tool_button_annotation, varray(""));
// Export grouping annotations.
register_annotation(MethodInfo("@export_category", PropertyInfo(Variant::STRING, "name")), AnnotationInfo::STANDALONE, &GDScriptParser::export_group_annotations<PROPERTY_USAGE_CATEGORY>);
register_annotation(MethodInfo("@export_group", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::STRING, "prefix")), AnnotationInfo::STANDALONE, &GDScriptParser::export_group_annotations<PROPERTY_USAGE_GROUP>, varray(""));
@@ -4618,10 +4619,10 @@ bool GDScriptParser::export_annotations(AnnotationNode *p_annotation, Node *p_ta
// For `@export_storage` and `@export_custom`, there is no need to check the variable type, argument values,
// or handle array exports in a special way, so they are implemented as separate methods.
-bool GDScriptParser::export_storage_annotation(AnnotationNode *p_annotation, Node *p_node, ClassNode *p_class) {
- ERR_FAIL_COND_V_MSG(p_node->type != Node::VARIABLE, false, vformat(R"("%s" annotation can only be applied to variables.)", p_annotation->name));
+bool GDScriptParser::export_storage_annotation(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) {
+ ERR_FAIL_COND_V_MSG(p_target->type != Node::VARIABLE, false, vformat(R"("%s" annotation can only be applied to variables.)", p_annotation->name));
- VariableNode *variable = static_cast<VariableNode *>(p_node);
+ VariableNode *variable = static_cast<VariableNode *>(p_target);
if (variable->is_static) {
push_error(vformat(R"(Annotation "%s" cannot be applied to a static variable.)", p_annotation->name), p_annotation);
return false;
@@ -4640,11 +4641,11 @@ bool GDScriptParser::export_storage_annotation(AnnotationNode *p_annotation, Nod
return true;
}
-bool GDScriptParser::export_custom_annotation(AnnotationNode *p_annotation, Node *p_node, ClassNode *p_class) {
- ERR_FAIL_COND_V_MSG(p_node->type != Node::VARIABLE, false, vformat(R"("%s" annotation can only be applied to variables.)", p_annotation->name));
+bool GDScriptParser::export_custom_annotation(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) {
+ ERR_FAIL_COND_V_MSG(p_target->type != Node::VARIABLE, false, vformat(R"("%s" annotation can only be applied to variables.)", p_annotation->name));
ERR_FAIL_COND_V_MSG(p_annotation->resolved_arguments.size() < 2, false, R"(Annotation "@export_custom" requires 2 arguments.)");
- VariableNode *variable = static_cast<VariableNode *>(p_node);
+ VariableNode *variable = static_cast<VariableNode *>(p_target);
if (variable->is_static) {
push_error(vformat(R"(Annotation "%s" cannot be applied to a static variable.)", p_annotation->name), p_annotation);
return false;
@@ -4668,12 +4669,56 @@ bool GDScriptParser::export_custom_annotation(AnnotationNode *p_annotation, Node
return true;
}
-template <PropertyUsageFlags t_usage>
-bool GDScriptParser::export_group_annotations(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) {
- if (p_annotation->resolved_arguments.is_empty()) {
+bool GDScriptParser::export_tool_button_annotation(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) {
+#ifdef TOOLS_ENABLED
+ ERR_FAIL_COND_V_MSG(p_target->type != Node::VARIABLE, false, vformat(R"("%s" annotation can only be applied to variables.)", p_annotation->name));
+ ERR_FAIL_COND_V(p_annotation->resolved_arguments.is_empty(), false);
+
+ if (!is_tool()) {
+ push_error(R"(Tool buttons can only be used in tool scripts (add "@tool" to the top of the script).)", p_annotation);
+ return false;
+ }
+
+ VariableNode *variable = static_cast<VariableNode *>(p_target);
+
+ if (variable->is_static) {
+ push_error(vformat(R"(Annotation "%s" cannot be applied to a static variable.)", p_annotation->name), p_annotation);
+ return false;
+ }
+ if (variable->exported) {
+ push_error(vformat(R"(Annotation "%s" cannot be used with another "@export" annotation.)", p_annotation->name), p_annotation);
return false;
}
+ const DataType variable_type = variable->get_datatype();
+ if (!variable_type.is_variant() && variable_type.is_hard_type()) {
+ if (variable_type.kind != DataType::BUILTIN || variable_type.builtin_type != Variant::CALLABLE) {
+ push_error(vformat(R"("@export_tool_button" annotation requires a variable of type "Callable", but type "%s" was given instead.)", variable_type.to_string()), p_annotation);
+ return false;
+ }
+ }
+
+ variable->exported = true;
+
+ // Build the hint string (format: `<text>[,<icon>]`).
+ String hint_string = p_annotation->resolved_arguments[0].operator String(); // Button text.
+ if (p_annotation->resolved_arguments.size() > 1) {
+ hint_string += "," + p_annotation->resolved_arguments[1].operator String(); // Button icon.
+ }
+
+ variable->export_info.type = Variant::CALLABLE;
+ variable->export_info.hint = PROPERTY_HINT_TOOL_BUTTON;
+ variable->export_info.hint_string = hint_string;
+ variable->export_info.usage = PROPERTY_USAGE_EDITOR;
+#endif // TOOLS_ENABLED
+
+ return true; // Only available in editor.
+}
+
+template <PropertyUsageFlags t_usage>
+bool GDScriptParser::export_group_annotations(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class) {
+ ERR_FAIL_COND_V(p_annotation->resolved_arguments.is_empty(), false);
+
p_annotation->export_info.name = p_annotation->resolved_arguments[0];
switch (t_usage) {
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index 7840474a89..7f64ae902b 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -1507,6 +1507,7 @@ private:
bool export_annotations(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
bool export_storage_annotation(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
bool export_custom_annotation(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
+ bool export_tool_button_annotation(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
template <PropertyUsageFlags t_usage>
bool export_group_annotations(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
bool warning_annotations(AnnotationNode *p_annotation, Node *p_target, ClassNode *p_class);
diff --git a/modules/gdscript/tests/scripts/parser/errors/export_tool_button_requires_tool_mode.gd b/modules/gdscript/tests/scripts/parser/errors/export_tool_button_requires_tool_mode.gd
new file mode 100644
index 0000000000..48be5b2541
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/export_tool_button_requires_tool_mode.gd
@@ -0,0 +1 @@
+@export_tool_button("Click me!") var action
diff --git a/modules/gdscript/tests/scripts/parser/errors/export_tool_button_requires_tool_mode.out b/modules/gdscript/tests/scripts/parser/errors/export_tool_button_requires_tool_mode.out
new file mode 100644
index 0000000000..fb148308e4
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/export_tool_button_requires_tool_mode.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Tool buttons can only be used in tool scripts (add "@tool" to the top of the script).
diff --git a/modules/gdscript/tests/scripts/parser/features/export_variable.gd b/modules/gdscript/tests/scripts/parser/features/export_variable.gd
index 1e134d0e0e..8aa449f602 100644
--- a/modules/gdscript/tests/scripts/parser/features/export_variable.gd
+++ b/modules/gdscript/tests/scripts/parser/features/export_variable.gd
@@ -1,3 +1,4 @@
+@tool
class_name ExportVariableTest
extends Node
@@ -47,6 +48,10 @@ const PreloadedUnnamedClass = preload("./export_variable_unnamed.notest.gd")
@export_custom(PROPERTY_HINT_ENUM, "A,B,C") var test_export_custom_weak_int = 5
@export_custom(PROPERTY_HINT_ENUM, "A,B,C") var test_export_custom_hard_int: int = 6
+# `@export_tool_button`.
+@export_tool_button("Click me!") var test_tool_button_1: Callable
+@export_tool_button("Click me!", "ColorRect") var test_tool_button_2: Callable
+
func test():
for property in get_property_list():
if str(property.name).begins_with("test_"):
diff --git a/modules/gdscript/tests/scripts/parser/features/export_variable.out b/modules/gdscript/tests/scripts/parser/features/export_variable.out
index d10462bb8d..0d915e00e6 100644
--- a/modules/gdscript/tests/scripts/parser/features/export_variable.out
+++ b/modules/gdscript/tests/scripts/parser/features/export_variable.out
@@ -55,3 +55,7 @@ var test_export_custom_weak_int: int = 5
hint=ENUM hint_string="A,B,C" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
var test_export_custom_hard_int: int = 6
hint=ENUM hint_string="A,B,C" usage=DEFAULT|SCRIPT_VARIABLE class_name=&""
+var test_tool_button_1: Callable = Callable()
+ hint=TOOL_BUTTON hint_string="Click me!" usage=EDITOR|SCRIPT_VARIABLE class_name=&""
+var test_tool_button_2: Callable = Callable()
+ hint=TOOL_BUTTON hint_string="Click me!,ColorRect" usage=EDITOR|SCRIPT_VARIABLE class_name=&""
diff --git a/modules/gdscript/tests/scripts/utils.notest.gd b/modules/gdscript/tests/scripts/utils.notest.gd
index 1e2788f765..fa289e442f 100644
--- a/modules/gdscript/tests/scripts/utils.notest.gd
+++ b/modules/gdscript/tests/scripts/utils.notest.gd
@@ -205,6 +205,9 @@ static func get_property_hint_name(hint: PropertyHint) -> String:
return "PROPERTY_HINT_HIDE_QUATERNION_EDIT"
PROPERTY_HINT_PASSWORD:
return "PROPERTY_HINT_PASSWORD"
+ PROPERTY_HINT_TOOL_BUTTON:
+ return "PROPERTY_HINT_TOOL_BUTTON"
+
printerr("Argument `hint` is invalid. Use `PROPERTY_HINT_*` constants.")
return "<invalid hint>"
diff --git a/modules/mbedtls/SCsub b/modules/mbedtls/SCsub
index e217ca5ca4..6183fa5944 100644
--- a/modules/mbedtls/SCsub
+++ b/modules/mbedtls/SCsub
@@ -66,6 +66,22 @@ if env["builtin_mbedtls"]:
"platform.c",
"platform_util.c",
"poly1305.c",
+ "psa_crypto.c",
+ "psa_crypto_aead.c",
+ "psa_crypto_cipher.c",
+ "psa_crypto_client.c",
+ "psa_crypto_driver_wrappers_no_static.c",
+ "psa_crypto_ecp.c",
+ "psa_crypto_ffdh.c",
+ "psa_crypto_hash.c",
+ "psa_crypto_mac.c",
+ "psa_crypto_pake.c",
+ "psa_crypto_rsa.c",
+ "psa_crypto_se.c",
+ "psa_crypto_slot_management.c",
+ "psa_crypto_storage.c",
+ "psa_its_file.c",
+ "psa_util.c",
"ripemd160.c",
"rsa.c",
"rsa_alt_helpers.c",
diff --git a/modules/mbedtls/crypto_mbedtls.cpp b/modules/mbedtls/crypto_mbedtls.cpp
index 0d97b5fc1a..be7aaef9d4 100644
--- a/modules/mbedtls/crypto_mbedtls.cpp
+++ b/modules/mbedtls/crypto_mbedtls.cpp
@@ -314,10 +314,6 @@ Crypto *CryptoMbedTLS::create(bool p_notify_postinitialize) {
}
void CryptoMbedTLS::initialize_crypto() {
-#ifdef DEBUG_ENABLED
- mbedtls_debug_set_threshold(1);
-#endif
-
Crypto::_create = create;
Crypto::_load_default_certificates = load_default_certificates;
X509CertificateMbedTLS::make_default();
diff --git a/modules/mbedtls/register_types.cpp b/modules/mbedtls/register_types.cpp
index df5bce05e4..bf65dfb0b7 100644
--- a/modules/mbedtls/register_types.cpp
+++ b/modules/mbedtls/register_types.cpp
@@ -35,15 +35,34 @@
#include "packet_peer_mbed_dtls.h"
#include "stream_peer_mbedtls.h"
+#if MBEDTLS_VERSION_MAJOR >= 3
+#include <psa/crypto.h>
+#endif
+
#ifdef TESTS_ENABLED
#include "tests/test_crypto_mbedtls.h"
#endif
+static bool godot_mbedtls_initialized = false;
+
void initialize_mbedtls_module(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
return;
}
+#if MBEDTLS_VERSION_MAJOR >= 3
+ int status = psa_crypto_init();
+ ERR_FAIL_COND_MSG(status != PSA_SUCCESS, "Failed to initialize psa crypto. The mbedTLS modules will not work.");
+#endif
+
+#ifdef DEBUG_ENABLED
+ if (OS::get_singleton()->is_stdout_verbose()) {
+ mbedtls_debug_set_threshold(1);
+ }
+#endif
+
+ godot_mbedtls_initialized = true;
+
CryptoMbedTLS::initialize_crypto();
StreamPeerMbedTLS::initialize_tls();
PacketPeerMbedDTLS::initialize_dtls();
@@ -55,6 +74,14 @@ void uninitialize_mbedtls_module(ModuleInitializationLevel p_level) {
return;
}
+ if (!godot_mbedtls_initialized) {
+ return;
+ }
+
+#if MBEDTLS_VERSION_MAJOR >= 3
+ mbedtls_psa_crypto_free();
+#endif
+
DTLSServerMbedTLS::finalize();
PacketPeerMbedDTLS::finalize_dtls();
StreamPeerMbedTLS::finalize_tls();
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/EventSignals_ScriptSignals.generated.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/EventSignals_ScriptSignals.generated.cs
index cc45e5746f..188972e6fe 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/EventSignals_ScriptSignals.generated.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/EventSignals_ScriptSignals.generated.cs
@@ -32,6 +32,10 @@ partial class EventSignals
add => backing_MySignal += value;
remove => backing_MySignal -= value;
}
+ protected void OnMySignal(string str, int num)
+ {
+ EmitSignal(SignalName.MySignal, str, num);
+ }
/// <inheritdoc/>
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
protected override void RaiseGodotClassSignalCallbacks(in godot_string_name signal, NativeVariantPtrArgs args)
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs
index 957d5789df..62fa7b0a36 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs
@@ -155,6 +155,32 @@ namespace Godot.SourceGenerators
};
}
+ public static string GetAccessibilityKeyword(this INamedTypeSymbol namedTypeSymbol)
+ {
+ if (namedTypeSymbol.DeclaredAccessibility == Accessibility.NotApplicable)
+ {
+ // Accessibility not specified. Get the default accessibility.
+ return namedTypeSymbol.ContainingSymbol switch
+ {
+ null or INamespaceSymbol => "internal",
+ ITypeSymbol { TypeKind: TypeKind.Class or TypeKind.Struct } => "private",
+ ITypeSymbol { TypeKind: TypeKind.Interface } => "public",
+ _ => "",
+ };
+ }
+
+ return namedTypeSymbol.DeclaredAccessibility switch
+ {
+ Accessibility.Private => "private",
+ Accessibility.Protected => "protected",
+ Accessibility.Internal => "internal",
+ Accessibility.ProtectedAndInternal => "private",
+ Accessibility.ProtectedOrInternal => "private",
+ Accessibility.Public => "public",
+ _ => "",
+ };
+ }
+
public static string NameWithTypeParameters(this INamedTypeSymbol symbol)
{
return symbol.IsGenericType ?
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs
index 54f2212339..0dda43ab4c 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs
@@ -5,13 +5,6 @@ using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
-// TODO:
-// Determine a proper way to emit the signal.
-// 'Emit(nameof(TheEvent))' creates a StringName every time and has the overhead of string marshaling.
-// I haven't decided on the best option yet. Some possibilities:
-// - Expose the generated StringName fields to the user, for use with 'Emit(...)'.
-// - Generate a 'EmitSignalName' method for each event signal.
-
namespace Godot.SourceGenerators
{
[Generator]
@@ -276,7 +269,7 @@ namespace Godot.SourceGenerators
source.Append(
$" /// <inheritdoc cref=\"{signalDelegate.DelegateSymbol.FullQualifiedNameIncludeGlobal()}\"/>\n");
- source.Append(" public event ")
+ source.Append($" {signalDelegate.DelegateSymbol.GetAccessibilityKeyword()} event ")
.Append(signalDelegate.DelegateSymbol.FullQualifiedNameIncludeGlobal())
.Append(" @")
.Append(signalName)
@@ -288,6 +281,43 @@ namespace Godot.SourceGenerators
.Append(signalName)
.Append(" -= value;\n")
.Append("}\n");
+
+ // Generate On{EventName} method to raise the event
+
+ var invokeMethodSymbol = signalDelegate.InvokeMethodData.Method;
+ int paramCount = invokeMethodSymbol.Parameters.Length;
+
+ string raiseMethodModifiers = signalDelegate.DelegateSymbol.ContainingType.IsSealed ?
+ "private" :
+ "protected";
+
+ source.Append($" {raiseMethodModifiers} void On{signalName}(");
+ for (int i = 0; i < paramCount; i++)
+ {
+ var paramSymbol = invokeMethodSymbol.Parameters[i];
+ source.Append($"{paramSymbol.Type.FullQualifiedNameIncludeGlobal()} {paramSymbol.Name}");
+ if (i < paramCount - 1)
+ {
+ source.Append(", ");
+ }
+ }
+ source.Append(")\n");
+ source.Append(" {\n");
+ source.Append($" EmitSignal(SignalName.{signalName}");
+ foreach (var paramSymbol in invokeMethodSymbol.Parameters)
+ {
+ // Enums must be converted to the underlying type before they can be implicitly converted to Variant
+ if (paramSymbol.Type.TypeKind == TypeKind.Enum)
+ {
+ var underlyingType = ((INamedTypeSymbol)paramSymbol.Type).EnumUnderlyingType;
+ source.Append($", ({underlyingType.FullQualifiedNameIncludeGlobal()}){paramSymbol.Name}");
+ continue;
+ }
+
+ source.Append($", {paramSymbol.Name}");
+ }
+ source.Append(");\n");
+ source.Append(" }\n");
}
// Generate RaiseGodotClassSignalCallbacks
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index a467aae2e9..89655e0b56 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -3227,6 +3227,46 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf
}
p_output.append(CLOSE_BLOCK_L1);
+
+ // Generate On{EventName} method to raise the event.
+ if (!p_itype.is_singleton) {
+ p_output.append(MEMBER_BEGIN "protected void ");
+ p_output << "On" << p_isignal.proxy_name;
+ if (is_parameterless) {
+ p_output.append("()\n" OPEN_BLOCK_L1 INDENT2);
+ p_output << "EmitSignal(SignalName." << p_isignal.proxy_name << ");\n";
+ p_output.append(CLOSE_BLOCK_L1);
+ } else {
+ p_output.append("(");
+
+ StringBuilder cs_emitsignal_params;
+
+ int idx = 0;
+ for (const ArgumentInterface &iarg : p_isignal.arguments) {
+ const TypeInterface *arg_type = _get_type_or_null(iarg.type);
+ ERR_FAIL_NULL_V_MSG(arg_type, ERR_BUG, "Argument type '" + iarg.type.cname + "' was not found.");
+
+ if (idx != 0) {
+ p_output << ", ";
+ cs_emitsignal_params << ", ";
+ }
+
+ p_output << arg_type->cs_type << " " << iarg.name;
+
+ if (arg_type->is_enum) {
+ cs_emitsignal_params << "(long)";
+ }
+
+ cs_emitsignal_params << iarg.name;
+
+ idx++;
+ }
+
+ p_output.append(")\n" OPEN_BLOCK_L1 INDENT2);
+ p_output << "EmitSignal(SignalName." << p_isignal.proxy_name << ", " << cs_emitsignal_params << ");\n";
+ p_output.append(CLOSE_BLOCK_L1);
+ }
+ }
}
return OK;
diff --git a/modules/multiplayer/doc_classes/MultiplayerSpawner.xml b/modules/multiplayer/doc_classes/MultiplayerSpawner.xml
index b620292519..198e2378c9 100644
--- a/modules/multiplayer/doc_classes/MultiplayerSpawner.xml
+++ b/modules/multiplayer/doc_classes/MultiplayerSpawner.xml
@@ -63,13 +63,13 @@
<signal name="despawned">
<param index="0" name="node" type="Node" />
<description>
- Emitted when a spawnable scene or custom spawn was despawned by the multiplayer authority. Only called on puppets.
+ Emitted when a spawnable scene or custom spawn was despawned by the multiplayer authority. Only called on remote peers.
</description>
</signal>
<signal name="spawned">
<param index="0" name="node" type="Node" />
<description>
- Emitted when a spawnable scene or custom spawn was spawned by the multiplayer authority. Only called on puppets.
+ Emitted when a spawnable scene or custom spawn was spawned by the multiplayer authority. Only called on remote peers.
</description>
</signal>
</signals>
diff --git a/modules/squish/SCsub b/modules/squish/SCsub
deleted file mode 100644
index d8e7fbc142..0000000000
--- a/modules/squish/SCsub
+++ /dev/null
@@ -1,45 +0,0 @@
-#!/usr/bin/env python
-from misc.utility.scons_hints import *
-
-Import("env")
-Import("env_modules")
-
-env_squish = env_modules.Clone()
-
-# Thirdparty source files
-
-thirdparty_obj = []
-
-if env["builtin_squish"]:
- thirdparty_dir = "#thirdparty/squish/"
- thirdparty_sources = [
- "alpha.cpp",
- "clusterfit.cpp",
- "colourblock.cpp",
- "colourfit.cpp",
- "colourset.cpp",
- "maths.cpp",
- "rangefit.cpp",
- "singlecolourfit.cpp",
- "squish.cpp",
- ]
-
- thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
-
- env_squish.Prepend(CPPPATH=[thirdparty_dir])
-
- env_thirdparty = env_squish.Clone()
- env_thirdparty.disable_warnings()
- env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
- env.modules_sources += thirdparty_obj
-
-
-# Godot source files
-
-module_obj = []
-
-env_squish.add_source_files(module_obj, "*.cpp")
-env.modules_sources += module_obj
-
-# Needed to force rebuilding the module files when the thirdparty library is updated.
-env.Depends(module_obj, thirdparty_obj)
diff --git a/modules/vorbis/resource_importer_ogg_vorbis.cpp b/modules/vorbis/resource_importer_ogg_vorbis.cpp
index 7b8d14741b..729a6f5561 100644
--- a/modules/vorbis/resource_importer_ogg_vorbis.cpp
+++ b/modules/vorbis/resource_importer_ogg_vorbis.cpp
@@ -155,7 +155,6 @@ Ref<AudioStreamOggVorbis> ResourceImporterOggVorbis::load_from_buffer(const Vect
char *sync_buf = ogg_sync_buffer(&sync_state, OGG_SYNC_BUFFER_SIZE);
err = ogg_sync_check(&sync_state);
ERR_FAIL_COND_V_MSG(err != 0, Ref<AudioStreamOggVorbis>(), "Ogg sync error " + itos(err));
- ERR_FAIL_COND_V(cursor > size_t(file_data.size()), Ref<AudioStreamOggVorbis>());
size_t copy_size = file_data.size() - cursor;
if (copy_size > OGG_SYNC_BUFFER_SIZE) {
copy_size = OGG_SYNC_BUFFER_SIZE;
diff --git a/modules/websocket/editor/editor_debugger_server_websocket.cpp b/modules/websocket/editor/editor_debugger_server_websocket.cpp
index a28fc53440..344a0356c5 100644
--- a/modules/websocket/editor/editor_debugger_server_websocket.cpp
+++ b/modules/websocket/editor/editor_debugger_server_websocket.cpp
@@ -77,8 +77,8 @@ Error EditorDebuggerServerWebSocket::start(const String &p_uri) {
// Optionally override
if (!p_uri.is_empty() && p_uri != "ws://") {
- String scheme, path;
- Error err = p_uri.parse_url(scheme, bind_host, bind_port, path);
+ String scheme, path, fragment;
+ Error err = p_uri.parse_url(scheme, bind_host, bind_port, path, fragment);
ERR_FAIL_COND_V(err != OK, ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V(!bind_host.is_valid_ip_address() && bind_host != "*", ERR_INVALID_PARAMETER);
}
diff --git a/modules/websocket/emws_peer.cpp b/modules/websocket/emws_peer.cpp
index 03a530909b..c5768c9f0b 100644
--- a/modules/websocket/emws_peer.cpp
+++ b/modules/websocket/emws_peer.cpp
@@ -68,8 +68,9 @@ Error EMWSPeer::connect_to_url(const String &p_url, Ref<TLSOptions> p_tls_option
String host;
String path;
String scheme;
+ String fragment;
int port = 0;
- Error err = p_url.parse_url(scheme, host, port, path);
+ Error err = p_url.parse_url(scheme, host, port, path, fragment);
ERR_FAIL_COND_V_MSG(err != OK, err, "Invalid URL: " + p_url);
if (scheme.is_empty()) {
diff --git a/modules/websocket/wsl_peer.cpp b/modules/websocket/wsl_peer.cpp
index 0a9a4053e3..0c0a046805 100644
--- a/modules/websocket/wsl_peer.cpp
+++ b/modules/websocket/wsl_peer.cpp
@@ -482,8 +482,9 @@ Error WSLPeer::connect_to_url(const String &p_url, Ref<TLSOptions> p_options) {
String host;
String path;
String scheme;
+ String fragment;
int port = 0;
- Error err = p_url.parse_url(scheme, host, port, path);
+ Error err = p_url.parse_url(scheme, host, port, path, fragment);
ERR_FAIL_COND_V_MSG(err != OK, err, "Invalid URL: " + p_url);
if (scheme.is_empty()) {
scheme = "ws://";
diff --git a/platform/android/doc_classes/EditorExportPlatformAndroid.xml b/platform/android/doc_classes/EditorExportPlatformAndroid.xml
index 020e432155..2fe5539e56 100644
--- a/platform/android/doc_classes/EditorExportPlatformAndroid.xml
+++ b/platform/android/doc_classes/EditorExportPlatformAndroid.xml
@@ -102,6 +102,9 @@
<member name="launcher_icons/adaptive_foreground_432x432" type="String" setter="" getter="">
Foreground layer of the application adaptive icon file. See [url=https://developer.android.com/develop/ui/views/launch/icon_design_adaptive#design-adaptive-icons]Design adaptive icons[/url].
</member>
+ <member name="launcher_icons/adaptive_monochrome_432x432" type="String" setter="" getter="">
+ Monochrome layer of the application adaptive icon file. See [url=https://developer.android.com/develop/ui/views/launch/icon_design_adaptive#design-adaptive-icons]Design adaptive icons[/url].
+ </member>
<member name="launcher_icons/main_192x192" type="String" setter="" getter="">
Application icon file. If left empty, it will fallback to [member ProjectSettings.application/config/icon].
</member>
diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp
index e3d9807af7..cfd258cddc 100644
--- a/platform/android/export/export_plugin.cpp
+++ b/platform/android/export/export_plugin.cpp
@@ -222,6 +222,7 @@ static const int icon_densities_count = 6;
static const char *launcher_icon_option = PNAME("launcher_icons/main_192x192");
static const char *launcher_adaptive_icon_foreground_option = PNAME("launcher_icons/adaptive_foreground_432x432");
static const char *launcher_adaptive_icon_background_option = PNAME("launcher_icons/adaptive_background_432x432");
+static const char *launcher_adaptive_icon_monochrome_option = PNAME("launcher_icons/adaptive_monochrome_432x432");
static const LauncherIcon launcher_icons[icon_densities_count] = {
{ "res/mipmap-xxxhdpi-v4/icon.png", 192 },
@@ -250,6 +251,15 @@ static const LauncherIcon launcher_adaptive_icon_backgrounds[icon_densities_coun
{ "res/mipmap/icon_background.png", 432 }
};
+static const LauncherIcon launcher_adaptive_icon_monochromes[icon_densities_count] = {
+ { "res/mipmap-xxxhdpi-v4/icon_monochrome.png", 432 },
+ { "res/mipmap-xxhdpi-v4/icon_monochrome.png", 324 },
+ { "res/mipmap-xhdpi-v4/icon_monochrome.png", 216 },
+ { "res/mipmap-hdpi-v4/icon_monochrome.png", 162 },
+ { "res/mipmap-mdpi-v4/icon_monochrome.png", 108 },
+ { "res/mipmap/icon_monochrome.png", 432 }
+};
+
static const int EXPORT_FORMAT_APK = 0;
static const int EXPORT_FORMAT_AAB = 1;
@@ -1644,12 +1654,13 @@ void EditorExportPlatformAndroid::_process_launcher_icons(const String &p_file_n
}
}
-void EditorExportPlatformAndroid::load_icon_refs(const Ref<EditorExportPreset> &p_preset, Ref<Image> &icon, Ref<Image> &foreground, Ref<Image> &background) {
+void EditorExportPlatformAndroid::load_icon_refs(const Ref<EditorExportPreset> &p_preset, Ref<Image> &icon, Ref<Image> &foreground, Ref<Image> &background, Ref<Image> &monochrome) {
String project_icon_path = GLOBAL_GET("application/config/icon");
icon.instantiate();
foreground.instantiate();
background.instantiate();
+ monochrome.instantiate();
// Regular icon: user selection -> project icon -> default.
String path = static_cast<String>(p_preset->get(launcher_icon_option)).strip_edges();
@@ -1677,12 +1688,20 @@ void EditorExportPlatformAndroid::load_icon_refs(const Ref<EditorExportPreset> &
print_verbose("Loading adaptive background icon from " + path);
ImageLoader::load_image(path, background);
}
+
+ // Adaptive monochrome: user selection -> default.
+ path = static_cast<String>(p_preset->get(launcher_adaptive_icon_monochrome_option)).strip_edges();
+ if (!path.is_empty()) {
+ print_verbose("Loading adaptive monochrome icon from " + path);
+ ImageLoader::load_image(path, background);
+ }
}
void EditorExportPlatformAndroid::_copy_icons_to_gradle_project(const Ref<EditorExportPreset> &p_preset,
const Ref<Image> &p_main_image,
const Ref<Image> &p_foreground,
- const Ref<Image> &p_background) {
+ const Ref<Image> &p_background,
+ const Ref<Image> &p_monochrome) {
String gradle_build_dir = ExportTemplateManager::get_android_build_directory(p_preset);
// Prepare images to be resized for the icons. If some image ends up being uninitialized,
@@ -1711,6 +1730,14 @@ void EditorExportPlatformAndroid::_copy_icons_to_gradle_project(const Ref<Editor
launcher_adaptive_icon_backgrounds[i].dimensions, data);
store_file_at_path(gradle_build_dir.path_join(launcher_adaptive_icon_backgrounds[i].export_path), data);
}
+
+ if (p_monochrome.is_valid() && !p_monochrome->is_empty()) {
+ print_verbose("Processing launcher adaptive icon p_monochrome for dimension " + itos(launcher_adaptive_icon_monochromes[i].dimensions) + " into " + launcher_adaptive_icon_monochromes[i].export_path);
+ Vector<uint8_t> data;
+ _process_launcher_icons(launcher_adaptive_icon_monochromes[i].export_path, p_monochrome,
+ launcher_adaptive_icon_monochromes[i].dimensions, data);
+ store_file_at_path(gradle_build_dir.path_join(launcher_adaptive_icon_monochromes[i].export_path), data);
+ }
}
}
@@ -1875,6 +1902,7 @@ void EditorExportPlatformAndroid::get_export_options(List<ExportOption> *r_optio
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_icon_option, PROPERTY_HINT_FILE, "*.png"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_adaptive_icon_foreground_option, PROPERTY_HINT_FILE, "*.png"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_adaptive_icon_background_option, PROPERTY_HINT_FILE, "*.png"), ""));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, launcher_adaptive_icon_monochrome_option, PROPERTY_HINT_FILE, "*.png"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "graphics/opengl_debug"), false));
@@ -3035,8 +3063,9 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
Ref<Image> main_image;
Ref<Image> foreground;
Ref<Image> background;
+ Ref<Image> monochrome;
- load_icon_refs(p_preset, main_image, foreground, background);
+ load_icon_refs(p_preset, main_image, foreground, background, monochrome);
Vector<uint8_t> command_line_flags;
// Write command line flags into the command_line_flags variable.
@@ -3107,7 +3136,7 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
add_message(EXPORT_MESSAGE_ERROR, TTR("Export"), TTR("Unable to overwrite res/*.xml files with project name."));
}
// Copies the project icon files into the appropriate Gradle project directory.
- _copy_icons_to_gradle_project(p_preset, main_image, foreground, background);
+ _copy_icons_to_gradle_project(p_preset, main_image, foreground, background, monochrome);
// Write an AndroidManifest.xml file into the Gradle project directory.
_write_tmp_manifest(p_preset, p_give_internet, p_debug);
@@ -3446,6 +3475,11 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
_process_launcher_icons(file, background, launcher_adaptive_icon_backgrounds[i].dimensions, data);
}
}
+ if (monochrome.is_valid() && !monochrome->is_empty()) {
+ if (file == launcher_adaptive_icon_monochromes[i].export_path) {
+ _process_launcher_icons(file, monochrome, launcher_adaptive_icon_monochromes[i].dimensions, data);
+ }
+ }
}
}
diff --git a/platform/android/export/export_plugin.h b/platform/android/export/export_plugin.h
index 708288fbf4..7e1d626486 100644
--- a/platform/android/export/export_plugin.h
+++ b/platform/android/export/export_plugin.h
@@ -167,12 +167,13 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
void _process_launcher_icons(const String &p_file_name, const Ref<Image> &p_source_image, int dimension, Vector<uint8_t> &p_data);
- void load_icon_refs(const Ref<EditorExportPreset> &p_preset, Ref<Image> &icon, Ref<Image> &foreground, Ref<Image> &background);
+ void load_icon_refs(const Ref<EditorExportPreset> &p_preset, Ref<Image> &icon, Ref<Image> &foreground, Ref<Image> &background, Ref<Image> &monochrome);
void _copy_icons_to_gradle_project(const Ref<EditorExportPreset> &p_preset,
const Ref<Image> &p_main_image,
const Ref<Image> &p_foreground,
- const Ref<Image> &p_background);
+ const Ref<Image> &p_background,
+ const Ref<Image> &p_monochrome);
static void _create_editor_debug_keystore_if_needed();
diff --git a/platform/android/java/editor/src/horizonos/java/org/godotengine/editor/GodotEditor.kt b/platform/android/java/editor/src/horizonos/java/org/godotengine/editor/GodotEditor.kt
index 6cb08ae94b..9dc34f2267 100644
--- a/platform/android/java/editor/src/horizonos/java/org/godotengine/editor/GodotEditor.kt
+++ b/platform/android/java/editor/src/horizonos/java/org/godotengine/editor/GodotEditor.kt
@@ -45,14 +45,14 @@ open class GodotEditor : BaseGodotEditor() {
internal val XR_RUN_GAME_INFO = EditorWindowInfo(GodotXRGame::class.java, 1667, ":GodotXRGame")
- internal const val USE_SCENE_PERMISSION = "com.oculus.permission.USE_SCENE"
+ internal val USE_SCENE_PERMISSIONS = listOf("com.oculus.permission.USE_SCENE", "horizonos.permission.USE_SCENE")
}
override fun getExcludedPermissions(): MutableSet<String> {
val excludedPermissions = super.getExcludedPermissions()
// The USE_SCENE permission is requested when the "xr/openxr/enabled" project setting
// is enabled.
- excludedPermissions.add(USE_SCENE_PERMISSION)
+ excludedPermissions.addAll(USE_SCENE_PERMISSIONS)
return excludedPermissions
}
diff --git a/platform/android/java/editor/src/horizonos/java/org/godotengine/editor/GodotXRGame.kt b/platform/android/java/editor/src/horizonos/java/org/godotengine/editor/GodotXRGame.kt
index 5db2879aad..0c82791e89 100644
--- a/platform/android/java/editor/src/horizonos/java/org/godotengine/editor/GodotXRGame.kt
+++ b/platform/android/java/editor/src/horizonos/java/org/godotengine/editor/GodotXRGame.kt
@@ -69,7 +69,7 @@ open class GodotXRGame: GodotGame() {
val automaticPermissionsRequestEnabled = automaticallyRequestPermissionsSetting.isNullOrEmpty() ||
automaticallyRequestPermissionsSetting.toBoolean()
if (automaticPermissionsRequestEnabled) {
- permissionsToEnable.add(USE_SCENE_PERMISSION)
+ permissionsToEnable.addAll(USE_SCENE_PERMISSIONS)
}
}
diff --git a/platform/android/java/lib/res/mipmap-anydpi-v26/icon.xml b/platform/android/java/lib/res/mipmap-anydpi-v26/icon.xml
index cfdcca2ab5..bb2ae6bee5 100644
--- a/platform/android/java/lib/res/mipmap-anydpi-v26/icon.xml
+++ b/platform/android/java/lib/res/mipmap-anydpi-v26/icon.xml
@@ -2,4 +2,5 @@
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@mipmap/icon_background"/>
<foreground android:drawable="@mipmap/icon_foreground"/>
+ <monochrome android:drawable="@mipmap/icon_monochrome"/>
</adaptive-icon>
diff --git a/platform/android/java/lib/res/mipmap/icon_monochrome.png b/platform/android/java/lib/res/mipmap/icon_monochrome.png
new file mode 100644
index 0000000000..28f59ea119
--- /dev/null
+++ b/platform/android/java/lib/res/mipmap/icon_monochrome.png
Binary files differ
diff --git a/platform/linuxbsd/detect.py b/platform/linuxbsd/detect.py
index d1de760f34..a67434527c 100644
--- a/platform/linuxbsd/detect.py
+++ b/platform/linuxbsd/detect.py
@@ -256,10 +256,6 @@ def configure(env: "SConsEnvironment"):
if not env["builtin_enet"]:
env.ParseConfig("pkg-config libenet --cflags --libs")
- if not env["builtin_squish"]:
- # libsquish doesn't reliably install its .pc file, so some distros lack it.
- env.Append(LIBS=["libsquish"])
-
if not env["builtin_zstd"]:
env.ParseConfig("pkg-config libzstd --cflags --libs")
diff --git a/platform/web/detect.py b/platform/web/detect.py
index bf75c2f9fc..735e2eaf4f 100644
--- a/platform/web/detect.py
+++ b/platform/web/detect.py
@@ -199,6 +199,11 @@ def configure(env: "SConsEnvironment"):
cc_version = get_compiler_version(env)
cc_semver = (cc_version["major"], cc_version["minor"], cc_version["patch"])
+ # Minimum emscripten requirements.
+ if cc_semver < (3, 1, 62):
+ print_error("The minimum emscripten version to build Godot is 3.1.62, detected: %s.%s.%s" % cc_semver)
+ sys.exit(255)
+
env.Prepend(CPPPATH=["#platform/web"])
env.Append(CPPDEFINES=["WEB_ENABLED", "UNIX_ENABLED"])
@@ -210,14 +215,12 @@ def configure(env: "SConsEnvironment"):
env.Append(LINKFLAGS=["-sOFFSCREEN_FRAMEBUFFER=1"])
# Disables the use of *glGetProcAddress() which is inefficient.
# See https://emscripten.org/docs/tools_reference/settings_reference.html#gl-enable-get-proc-address
- if cc_semver >= (3, 1, 51):
- env.Append(LINKFLAGS=["-sGL_ENABLE_GET_PROC_ADDRESS=0"])
+ env.Append(LINKFLAGS=["-sGL_ENABLE_GET_PROC_ADDRESS=0"])
if env["javascript_eval"]:
env.Append(CPPDEFINES=["JAVASCRIPT_EVAL_ENABLED"])
- stack_size_opt = "STACK_SIZE" if cc_semver >= (3, 1, 25) else "TOTAL_STACK"
- env.Append(LINKFLAGS=["-s%s=%sKB" % (stack_size_opt, env["stack_size"])])
+ env.Append(LINKFLAGS=["-s%s=%sKB" % ("STACK_SIZE", env["stack_size"])])
if env["threads"]:
# Thread support (via SharedArrayBuffer).
@@ -237,30 +240,21 @@ def configure(env: "SConsEnvironment"):
env["proxy_to_pthread"] = False
if env["lto"] != "none":
- # Workaround https://github.com/emscripten-core/emscripten/issues/19781.
- if cc_semver >= (3, 1, 42) and cc_semver < (3, 1, 46):
- env.Append(LINKFLAGS=["-Wl,-u,scalbnf"])
# Workaround https://github.com/emscripten-core/emscripten/issues/16836.
- if cc_semver >= (3, 1, 47):
- env.Append(LINKFLAGS=["-Wl,-u,_emscripten_run_callback_on_thread"])
+ env.Append(LINKFLAGS=["-Wl,-u,_emscripten_run_callback_on_thread"])
if env["dlink_enabled"]:
if env["proxy_to_pthread"]:
print_warning("GDExtension support requires proxy_to_pthread=no, disabling proxy to pthread.")
env["proxy_to_pthread"] = False
- if cc_semver < (3, 1, 14):
- print_error("GDExtension support requires emscripten >= 3.1.14, detected: %s.%s.%s" % cc_semver)
- sys.exit(255)
-
env.Append(CCFLAGS=["-sSIDE_MODULE=2"])
env.Append(LINKFLAGS=["-sSIDE_MODULE=2"])
env.Append(CCFLAGS=["-fvisibility=hidden"])
env.Append(LINKFLAGS=["-fvisibility=hidden"])
env.extra_suffix = ".dlink" + env.extra_suffix
- # WASM_BIGINT is needed since emscripten ≥ 3.1.41
- needs_wasm_bigint = cc_semver >= (3, 1, 41)
+ env.Append(LINKFLAGS=["-sWASM_BIGINT"])
# Run the main application in a web worker
if env["proxy_to_pthread"]:
@@ -269,11 +263,6 @@ def configure(env: "SConsEnvironment"):
env.Append(LINKFLAGS=["-sEXPORTED_RUNTIME_METHODS=['_emscripten_proxy_main']"])
# https://github.com/emscripten-core/emscripten/issues/18034#issuecomment-1277561925
env.Append(LINKFLAGS=["-sTEXTDECODER=0"])
- # BigInt support to pass object pointers between contexts
- needs_wasm_bigint = True
-
- if needs_wasm_bigint:
- env.Append(LINKFLAGS=["-sWASM_BIGINT"])
# Reduce code size by generating less support code (e.g. skip NodeJS support).
env.Append(LINKFLAGS=["-sENVIRONMENT=web,worker"])
diff --git a/scene/3d/mesh_instance_3d.cpp b/scene/3d/mesh_instance_3d.cpp
index f551cb401c..e8166802f8 100644
--- a/scene/3d/mesh_instance_3d.cpp
+++ b/scene/3d/mesh_instance_3d.cpp
@@ -332,8 +332,8 @@ void MeshInstance3D::create_multiple_convex_collisions(const Ref<MeshConvexDecom
void MeshInstance3D::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_ENTER_TREE: {
- _resolve_skeleton_path();
+ case NOTIFICATION_READY: {
+ callable_mp(this, &MeshInstance3D::_resolve_skeleton_path).call_deferred();
} break;
case NOTIFICATION_TRANSLATION_CHANGED: {
if (mesh.is_valid()) {
diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp
index a96417738f..a2aef60417 100644
--- a/scene/animation/animation_blend_tree.cpp
+++ b/scene/animation/animation_blend_tree.cpp
@@ -245,6 +245,8 @@ AnimationNode::NodeTimeInfo AnimationNodeAnimation::_process(const AnimationMixe
if (!p_test_only) {
AnimationMixer::PlaybackInfo pi = p_playback_info;
+ pi.start = 0.0;
+ pi.end = cur_len;
if (play_mode == PLAY_MODE_FORWARD) {
pi.time = cur_playback_time;
pi.delta = cur_delta;
diff --git a/scene/animation/animation_mixer.cpp b/scene/animation/animation_mixer.cpp
index 664302d45e..eb8bc8c382 100644
--- a/scene/animation/animation_mixer.cpp
+++ b/scene/animation/animation_mixer.cpp
@@ -1117,6 +1117,8 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
Ref<Animation> a = ai.animation_data.animation;
double time = ai.playback_info.time;
double delta = ai.playback_info.delta;
+ double start = ai.playback_info.start;
+ double end = ai.playback_info.end;
bool seeked = ai.playback_info.seeked;
Animation::LoopedFlag looped_flag = ai.playback_info.looped_flag;
bool is_external_seeking = ai.playback_info.is_external_seeking;
@@ -1168,32 +1170,32 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
if (track->root_motion && calc_root) {
double prev_time = time - delta;
if (!backward) {
- if (Animation::is_less_approx(prev_time, 0)) {
+ if (Animation::is_less_approx(prev_time, start)) {
switch (a->get_loop_mode()) {
case Animation::LOOP_NONE: {
- prev_time = 0;
+ prev_time = start;
} break;
case Animation::LOOP_LINEAR: {
- prev_time = Math::fposmod(prev_time, (double)a_length);
+ prev_time = Math::fposmod(prev_time - start, end - start) + start;
} break;
case Animation::LOOP_PINGPONG: {
- prev_time = Math::pingpong(prev_time, (double)a_length);
+ prev_time = Math::pingpong(prev_time - start, end - start) + start;
} break;
default:
break;
}
}
} else {
- if (Animation::is_greater_approx(prev_time, (double)a_length)) {
+ if (Animation::is_greater_approx(prev_time, end)) {
switch (a->get_loop_mode()) {
case Animation::LOOP_NONE: {
- prev_time = (double)a_length;
+ prev_time = end;
} break;
case Animation::LOOP_LINEAR: {
- prev_time = Math::fposmod(prev_time, (double)a_length);
+ prev_time = Math::fposmod(prev_time - start, end - start) + start;
} break;
case Animation::LOOP_PINGPONG: {
- prev_time = Math::pingpong(prev_time, (double)a_length);
+ prev_time = Math::pingpong(prev_time - start, end - start) + start;
} break;
default:
break;
@@ -1208,10 +1210,10 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
continue;
}
loc[0] = post_process_key_value(a, i, loc[0], t->object_id, t->bone_idx);
- a->try_position_track_interpolate(i, (double)a_length, &loc[1]);
+ a->try_position_track_interpolate(i, end, &loc[1]);
loc[1] = post_process_key_value(a, i, loc[1], t->object_id, t->bone_idx);
root_motion_cache.loc += (loc[1] - loc[0]) * blend;
- prev_time = 0;
+ prev_time = start;
}
} else {
if (Animation::is_less_approx(prev_time, time)) {
@@ -1220,10 +1222,10 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
continue;
}
loc[0] = post_process_key_value(a, i, loc[0], t->object_id, t->bone_idx);
- a->try_position_track_interpolate(i, 0, &loc[1]);
+ a->try_position_track_interpolate(i, start, &loc[1]);
loc[1] = post_process_key_value(a, i, loc[1], t->object_id, t->bone_idx);
root_motion_cache.loc += (loc[1] - loc[0]) * blend;
- prev_time = (double)a_length;
+ prev_time = end;
}
}
Error err = a->try_position_track_interpolate(i, prev_time, &loc[0]);
@@ -1234,7 +1236,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
a->try_position_track_interpolate(i, time, &loc[1]);
loc[1] = post_process_key_value(a, i, loc[1], t->object_id, t->bone_idx);
root_motion_cache.loc += (loc[1] - loc[0]) * blend;
- prev_time = !backward ? 0 : (double)a_length;
+ prev_time = !backward ? start : end;
}
{
Vector3 loc;
@@ -1256,32 +1258,32 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
if (track->root_motion && calc_root) {
double prev_time = time - delta;
if (!backward) {
- if (Animation::is_less_approx(prev_time, 0)) {
+ if (Animation::is_less_approx(prev_time, start)) {
switch (a->get_loop_mode()) {
case Animation::LOOP_NONE: {
- prev_time = 0;
+ prev_time = start;
} break;
case Animation::LOOP_LINEAR: {
- prev_time = Math::fposmod(prev_time, (double)a_length);
+ prev_time = Math::fposmod(prev_time - start, end - start) + start;
} break;
case Animation::LOOP_PINGPONG: {
- prev_time = Math::pingpong(prev_time, (double)a_length);
+ prev_time = Math::pingpong(prev_time - start, end - start) + start;
} break;
default:
break;
}
}
} else {
- if (Animation::is_greater_approx(prev_time, (double)a_length)) {
+ if (Animation::is_greater_approx(prev_time, end)) {
switch (a->get_loop_mode()) {
case Animation::LOOP_NONE: {
- prev_time = (double)a_length;
+ prev_time = end;
} break;
case Animation::LOOP_LINEAR: {
- prev_time = Math::fposmod(prev_time, (double)a_length);
+ prev_time = Math::fposmod(prev_time - start, end - start) + start;
} break;
case Animation::LOOP_PINGPONG: {
- prev_time = Math::pingpong(prev_time, (double)a_length);
+ prev_time = Math::pingpong(prev_time - start, end - start) + start;
} break;
default:
break;
@@ -1296,10 +1298,10 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
continue;
}
rot[0] = post_process_key_value(a, i, rot[0], t->object_id, t->bone_idx);
- a->try_rotation_track_interpolate(i, (double)a_length, &rot[1]);
+ a->try_rotation_track_interpolate(i, end, &rot[1]);
rot[1] = post_process_key_value(a, i, rot[1], t->object_id, t->bone_idx);
root_motion_cache.rot = (root_motion_cache.rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized();
- prev_time = 0;
+ prev_time = start;
}
} else {
if (Animation::is_less_approx(prev_time, time)) {
@@ -1308,9 +1310,9 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
continue;
}
rot[0] = post_process_key_value(a, i, rot[0], t->object_id, t->bone_idx);
- a->try_rotation_track_interpolate(i, 0, &rot[1]);
+ a->try_rotation_track_interpolate(i, start, &rot[1]);
root_motion_cache.rot = (root_motion_cache.rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized();
- prev_time = (double)a_length;
+ prev_time = end;
}
}
Error err = a->try_rotation_track_interpolate(i, prev_time, &rot[0]);
@@ -1321,7 +1323,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
a->try_rotation_track_interpolate(i, time, &rot[1]);
rot[1] = post_process_key_value(a, i, rot[1], t->object_id, t->bone_idx);
root_motion_cache.rot = (root_motion_cache.rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized();
- prev_time = !backward ? 0 : (double)a_length;
+ prev_time = !backward ? start : end;
}
{
Quaternion rot;
@@ -1343,32 +1345,32 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
if (track->root_motion && calc_root) {
double prev_time = time - delta;
if (!backward) {
- if (Animation::is_less_approx(prev_time, 0)) {
+ if (Animation::is_less_approx(prev_time, start)) {
switch (a->get_loop_mode()) {
case Animation::LOOP_NONE: {
- prev_time = 0;
+ prev_time = start;
} break;
case Animation::LOOP_LINEAR: {
- prev_time = Math::fposmod(prev_time, (double)a_length);
+ prev_time = Math::fposmod(prev_time - start, end - start) + start;
} break;
case Animation::LOOP_PINGPONG: {
- prev_time = Math::pingpong(prev_time, (double)a_length);
+ prev_time = Math::pingpong(prev_time - start, end - start) + start;
} break;
default:
break;
}
}
} else {
- if (Animation::is_greater_approx(prev_time, (double)a_length)) {
+ if (Animation::is_greater_approx(prev_time, end)) {
switch (a->get_loop_mode()) {
case Animation::LOOP_NONE: {
- prev_time = (double)a_length;
+ prev_time = end;
} break;
case Animation::LOOP_LINEAR: {
- prev_time = Math::fposmod(prev_time, (double)a_length);
+ prev_time = Math::fposmod(prev_time - start, end - start) + start;
} break;
case Animation::LOOP_PINGPONG: {
- prev_time = Math::pingpong(prev_time, (double)a_length);
+ prev_time = Math::pingpong(prev_time - start, end - start) + start;
} break;
default:
break;
@@ -1383,10 +1385,10 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
continue;
}
scale[0] = post_process_key_value(a, i, scale[0], t->object_id, t->bone_idx);
- a->try_scale_track_interpolate(i, (double)a_length, &scale[1]);
+ a->try_scale_track_interpolate(i, end, &scale[1]);
root_motion_cache.scale += (scale[1] - scale[0]) * blend;
scale[1] = post_process_key_value(a, i, scale[1], t->object_id, t->bone_idx);
- prev_time = 0;
+ prev_time = start;
}
} else {
if (Animation::is_less_approx(prev_time, time)) {
@@ -1395,10 +1397,10 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
continue;
}
scale[0] = post_process_key_value(a, i, scale[0], t->object_id, t->bone_idx);
- a->try_scale_track_interpolate(i, 0, &scale[1]);
+ a->try_scale_track_interpolate(i, start, &scale[1]);
scale[1] = post_process_key_value(a, i, scale[1], t->object_id, t->bone_idx);
root_motion_cache.scale += (scale[1] - scale[0]) * blend;
- prev_time = (double)a_length;
+ prev_time = end;
}
}
Error err = a->try_scale_track_interpolate(i, prev_time, &scale[0]);
@@ -1409,7 +1411,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
a->try_scale_track_interpolate(i, time, &scale[1]);
scale[1] = post_process_key_value(a, i, scale[1], t->object_id, t->bone_idx);
root_motion_cache.scale += (scale[1] - scale[0]) * blend;
- prev_time = !backward ? 0 : (double)a_length;
+ prev_time = !backward ? start : end;
}
{
Vector3 scale;
@@ -1671,6 +1673,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
if (!player2) {
continue;
}
+ // TODO: Make it possible to embed section info in animation track keys.
if (seeked) {
// Seek.
int idx = a->track_find_key(i, time, Animation::FIND_MODE_NEAREST, true);
@@ -1683,19 +1686,19 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) {
continue;
}
Ref<Animation> anim = player2->get_animation(anim_name);
- double at_anim_pos = 0.0;
+ double at_anim_pos = start;
switch (anim->get_loop_mode()) {
case Animation::LOOP_NONE: {
- if (!is_external_seeking && ((!backward && Animation::is_greater_or_equal_approx(time, pos + (double)anim->get_length())) || (backward && Animation::is_less_or_equal_approx(time, pos)))) {
+ if (!is_external_seeking && ((!backward && Animation::is_greater_or_equal_approx(time, pos + end)) || (backward && Animation::is_less_or_equal_approx(time, pos + start)))) {
continue; // Do nothing if current time is outside of length when started.
}
- at_anim_pos = MIN((double)anim->get_length(), time - pos); // Seek to end.
+ at_anim_pos = MIN(end, time - pos); // Seek to end.
} break;
case Animation::LOOP_LINEAR: {
- at_anim_pos = Math::fposmod(time - pos, (double)anim->get_length()); // Seek to loop.
+ at_anim_pos = Math::fposmod(time - pos - start, end - start) + start; // Seek to loop.
} break;
case Animation::LOOP_PINGPONG: {
- at_anim_pos = Math::pingpong(time - pos, (double)a_length);
+ at_anim_pos = Math::pingpong(time - pos - start, end - start) + start;
} break;
default:
break;
@@ -2092,6 +2095,8 @@ Ref<AnimatedValuesBackup> AnimationMixer::make_backup() {
PlaybackInfo pi;
pi.time = 0;
pi.delta = 0;
+ pi.start = 0;
+ pi.end = reset_anim->get_length();
pi.seeked = true;
pi.weight = 1.0;
make_animation_instance(SceneStringName(RESET), pi);
diff --git a/scene/animation/animation_mixer.h b/scene/animation/animation_mixer.h
index 5482197fbd..27c9a00a9c 100644
--- a/scene/animation/animation_mixer.h
+++ b/scene/animation/animation_mixer.h
@@ -85,6 +85,8 @@ public:
struct PlaybackInfo {
double time = 0.0;
double delta = 0.0;
+ double start = 0.0;
+ double end = 0.0;
bool seeked = false;
bool is_external_seeking = false;
Animation::LoopedFlag looped_flag = Animation::LOOPED_FLAG_NONE;
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index a4aa383a9d..bc951e4e14 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -164,39 +164,41 @@ void AnimationPlayer::_process_playback_data(PlaybackData &cd, double p_delta, f
double delta = p_started ? 0 : p_delta * speed;
double next_pos = cd.pos + delta;
- double len = cd.from->animation->get_length();
+ double start = get_section_start_time();
+ double end = get_section_end_time();
+
Animation::LoopedFlag looped_flag = Animation::LOOPED_FLAG_NONE;
switch (cd.from->animation->get_loop_mode()) {
case Animation::LOOP_NONE: {
- if (Animation::is_less_approx(next_pos, 0)) {
- next_pos = 0;
- } else if (Animation::is_greater_approx(next_pos, len)) {
- next_pos = len;
+ if (Animation::is_less_approx(next_pos, start)) {
+ next_pos = start;
+ } else if (Animation::is_greater_approx(next_pos, end)) {
+ next_pos = end;
}
delta = next_pos - cd.pos; // Fix delta (after determination of backwards because negative zero is lost here).
} break;
case Animation::LOOP_LINEAR: {
- if (Animation::is_less_approx(next_pos, 0) && Animation::is_greater_or_equal_approx(cd.pos, 0)) {
+ if (Animation::is_less_approx(next_pos, start) && Animation::is_greater_or_equal_approx(cd.pos, start)) {
looped_flag = Animation::LOOPED_FLAG_START;
}
- if (Animation::is_greater_approx(next_pos, len) && Animation::is_less_or_equal_approx(cd.pos, len)) {
+ if (Animation::is_greater_approx(next_pos, end) && Animation::is_less_or_equal_approx(cd.pos, end)) {
looped_flag = Animation::LOOPED_FLAG_END;
}
- next_pos = Math::fposmod(next_pos, (double)len);
+ next_pos = Math::fposmod(next_pos - start, end - start) + start;
} break;
case Animation::LOOP_PINGPONG: {
- if (Animation::is_less_approx(next_pos, 0) && Animation::is_greater_or_equal_approx(cd.pos, 0)) {
+ if (Animation::is_less_approx(next_pos, start) && Animation::is_greater_or_equal_approx(cd.pos, start)) {
cd.speed_scale *= -1.0;
looped_flag = Animation::LOOPED_FLAG_START;
}
- if (Animation::is_greater_approx(next_pos, len) && Animation::is_less_or_equal_approx(cd.pos, len)) {
+ if (Animation::is_greater_approx(next_pos, end) && Animation::is_less_or_equal_approx(cd.pos, end)) {
cd.speed_scale *= -1.0;
looped_flag = Animation::LOOPED_FLAG_END;
}
- next_pos = Math::pingpong(next_pos, (double)len);
+ next_pos = Math::pingpong(next_pos - start, end - start) + start;
} break;
default:
@@ -208,18 +210,18 @@ void AnimationPlayer::_process_playback_data(PlaybackData &cd, double p_delta, f
// End detection.
if (p_is_current) {
if (cd.from->animation->get_loop_mode() == Animation::LOOP_NONE) {
- if (!backwards && Animation::is_less_or_equal_approx(prev_pos, len) && Math::is_equal_approx(next_pos, len)) {
+ if (!backwards && Animation::is_less_or_equal_approx(prev_pos, end) && Math::is_equal_approx(next_pos, end)) {
// Playback finished.
- next_pos = len; // Snap to the edge.
+ next_pos = end; // Snap to the edge.
end_reached = true;
- end_notify = Animation::is_less_approx(prev_pos, len); // Notify only if not already at the end.
+ end_notify = Animation::is_less_approx(prev_pos, end); // Notify only if not already at the end.
p_blend = 1.0;
}
- if (backwards && Animation::is_greater_or_equal_approx(prev_pos, 0) && Math::is_equal_approx(next_pos, 0)) {
+ if (backwards && Animation::is_greater_or_equal_approx(prev_pos, start) && Math::is_equal_approx(next_pos, start)) {
// Playback finished.
- next_pos = 0; // Snap to the edge.
+ next_pos = start; // Snap to the edge.
end_reached = true;
- end_notify = Animation::is_greater_approx(prev_pos, 0); // Notify only if not already at the beginning.
+ end_notify = Animation::is_greater_approx(prev_pos, start); // Notify only if not already at the beginning.
p_blend = 1.0;
}
}
@@ -231,10 +233,14 @@ void AnimationPlayer::_process_playback_data(PlaybackData &cd, double p_delta, f
if (p_started) {
pi.time = prev_pos;
pi.delta = 0;
+ pi.start = start;
+ pi.end = end;
pi.seeked = true;
} else {
pi.time = next_pos;
pi.delta = delta;
+ pi.start = start;
+ pi.end = end;
pi.seeked = p_seeked;
}
if (Math::is_zero_approx(pi.delta) && backwards) {
@@ -378,6 +384,14 @@ void AnimationPlayer::play_backwards(const StringName &p_name, double p_custom_b
play(p_name, p_custom_blend, -1, true);
}
+void AnimationPlayer::play_section_with_markers_backwards(const StringName &p_name, const StringName &p_start_marker, const StringName &p_end_marker, double p_custom_blend) {
+ play_section_with_markers(p_name, p_start_marker, p_end_marker, p_custom_blend, -1, true);
+}
+
+void AnimationPlayer::play_section_backwards(const StringName &p_name, double p_start_time, double p_end_time, double p_custom_blend) {
+ play_section(p_name, p_start_time, p_end_time, -1, true);
+}
+
void AnimationPlayer::play(const StringName &p_name, double p_custom_blend, float p_custom_scale, bool p_from_end) {
if (auto_capture) {
play_with_capture(p_name, auto_capture_duration, p_custom_blend, p_custom_scale, p_from_end, auto_capture_transition_type, auto_capture_ease_type);
@@ -387,6 +401,10 @@ void AnimationPlayer::play(const StringName &p_name, double p_custom_blend, floa
}
void AnimationPlayer::_play(const StringName &p_name, double p_custom_blend, float p_custom_scale, bool p_from_end) {
+ play_section_with_markers(p_name, StringName(), StringName(), p_custom_blend, p_custom_scale, p_from_end);
+}
+
+void AnimationPlayer::play_section_with_markers(const StringName &p_name, const StringName &p_start_marker, const StringName &p_end_marker, double p_custom_blend, float p_custom_scale, bool p_from_end) {
StringName name = p_name;
if (name == StringName()) {
@@ -395,6 +413,38 @@ void AnimationPlayer::_play(const StringName &p_name, double p_custom_blend, flo
ERR_FAIL_COND_MSG(!animation_set.has(name), vformat("Animation not found: %s.", name));
+ Ref<Animation> animation = animation_set[name].animation;
+
+ ERR_FAIL_COND_MSG(p_start_marker == p_end_marker && p_start_marker, vformat("Start marker and end marker cannot be the same marker: %s.", p_start_marker));
+ ERR_FAIL_COND_MSG(p_start_marker && !animation->has_marker(p_start_marker), vformat("Marker %s not found in animation: %s.", p_start_marker, name));
+ ERR_FAIL_COND_MSG(p_end_marker && !animation->has_marker(p_end_marker), vformat("Marker %s not found in animation: %s.", p_end_marker, name));
+
+ double start_time = p_start_marker ? animation->get_marker_time(p_start_marker) : -1;
+ double end_time = p_end_marker ? animation->get_marker_time(p_end_marker) : -1;
+
+ ERR_FAIL_COND_MSG(p_start_marker && p_end_marker && Animation::is_greater_approx(start_time, end_time), vformat("End marker %s is placed earlier than start marker %s in animation: %s.", p_end_marker, p_start_marker, name));
+
+ if (p_start_marker && Animation::is_less_approx(start_time, 0)) {
+ WARN_PRINT_ED(vformat("Negative time start marker: %s is invalid in the section, so the start of the animation: %s is used instead.", p_start_marker, playback.current.from->animation->get_name()));
+ }
+ if (p_end_marker && Animation::is_less_approx(end_time, 0)) {
+ WARN_PRINT_ED(vformat("Negative time end marker: %s is invalid in the section, so the end of the animation: %s is used instead.", p_end_marker, playback.current.from->animation->get_name()));
+ }
+
+ play_section(name, start_time, end_time, p_custom_blend, p_custom_scale, p_from_end);
+}
+
+void AnimationPlayer::play_section(const StringName &p_name, double p_start_time, double p_end_time, double p_custom_blend, float p_custom_scale, bool p_from_end) {
+ StringName name = p_name;
+
+ if (name == StringName()) {
+ name = playback.assigned;
+ }
+
+ ERR_FAIL_COND_MSG(!animation_set.has(name), vformat("Animation not found: %s.", name));
+ ERR_FAIL_COND_MSG(p_start_time >= 0 && p_end_time >= 0 && Math::is_equal_approx(p_start_time, p_end_time), "Start time and end time must not equal to each other.");
+ ERR_FAIL_COND_MSG(p_start_time >= 0 && p_end_time >= 0 && Animation::is_greater_approx(p_start_time, p_end_time), vformat("Start time %f is greater than end time %f.", p_start_time, p_end_time));
+
Playback &c = playback;
if (c.current.from) {
@@ -442,22 +492,27 @@ void AnimationPlayer::_play(const StringName &p_name, double p_custom_blend, flo
c.current.from = &animation_set[name];
c.current.speed_scale = p_custom_scale;
+ c.current.start_time = p_start_time;
+ c.current.end_time = p_end_time;
+
+ double start = get_section_start_time();
+ double end = get_section_end_time();
if (!end_reached) {
playback_queue.clear();
}
if (c.assigned != name) { // Reset.
- c.current.pos = p_from_end ? c.current.from->animation->get_length() : 0;
+ c.current.pos = p_from_end ? end : start;
c.assigned = name;
emit_signal(SNAME("current_animation_changed"), c.assigned);
} else {
- if (p_from_end && Math::is_zero_approx(c.current.pos)) {
+ if (p_from_end && Math::is_equal_approx(c.current.pos, start)) {
// Animation reset but played backwards, set position to the end.
- seek_internal(c.current.from->animation->get_length(), true, true, true);
- } else if (!p_from_end && Math::is_equal_approx(c.current.pos, (double)c.current.from->animation->get_length())) {
+ seek_internal(end, true, true, true);
+ } else if (!p_from_end && Math::is_equal_approx(c.current.pos, end)) {
// Animation resumed but already ended, set position to the beginning.
- seek_internal(0, true, true, true);
+ seek_internal(start, true, true, true);
} else if (playing) {
return;
}
@@ -551,6 +606,8 @@ void AnimationPlayer::set_assigned_animation(const String &p_animation) {
ERR_FAIL_COND_MSG(!animation_set.has(p_animation), vformat("Animation not found: %s.", p_animation));
playback.current.pos = 0;
playback.current.from = &animation_set[p_animation];
+ playback.current.start_time = -1;
+ playback.current.end_time = -1;
playback.assigned = p_animation;
emit_signal(SNAME("current_animation_changed"), playback.assigned);
}
@@ -603,6 +660,12 @@ void AnimationPlayer::seek_internal(double p_time, bool p_update, bool p_update_
}
}
+ double start = get_section_start_time();
+ double end = get_section_end_time();
+
+ // Clamp the seek position.
+ p_time = CLAMP(p_time, start, end);
+
playback.seeked = true;
playback.internal_seeked = p_is_internal_seek;
@@ -641,6 +704,55 @@ double AnimationPlayer::get_current_animation_length() const {
return playback.current.from->animation->get_length();
}
+void AnimationPlayer::set_section_with_markers(const StringName &p_start_marker, const StringName &p_end_marker) {
+ ERR_FAIL_NULL_MSG(playback.current.from, "AnimationPlayer has no current animation.");
+ ERR_FAIL_COND_MSG(p_start_marker == p_end_marker && p_start_marker, vformat("Start marker and end marker cannot be the same marker: %s.", p_start_marker));
+ ERR_FAIL_COND_MSG(p_start_marker && !playback.current.from->animation->has_marker(p_start_marker), vformat("Marker %s not found in animation: %s.", p_start_marker, playback.current.from->animation->get_name()));
+ ERR_FAIL_COND_MSG(p_end_marker && !playback.current.from->animation->has_marker(p_end_marker), vformat("Marker %s not found in animation: %s.", p_end_marker, playback.current.from->animation->get_name()));
+ double start_time = p_start_marker ? playback.current.from->animation->get_marker_time(p_start_marker) : -1;
+ double end_time = p_end_marker ? playback.current.from->animation->get_marker_time(p_end_marker) : -1;
+ if (p_start_marker && Animation::is_less_approx(start_time, 0)) {
+ WARN_PRINT_ONCE_ED(vformat("Marker %s time must be positive in animation: %s.", p_start_marker, playback.current.from->animation->get_name()));
+ }
+ if (p_end_marker && Animation::is_less_approx(end_time, 0)) {
+ WARN_PRINT_ONCE_ED(vformat("Marker %s time must be positive in animation: %s.", p_end_marker, playback.current.from->animation->get_name()));
+ }
+ set_section(start_time, end_time);
+}
+
+void AnimationPlayer::set_section(double p_start_time, double p_end_time) {
+ ERR_FAIL_NULL_MSG(playback.current.from, "AnimationPlayer has no current animation.");
+ ERR_FAIL_COND_MSG(Animation::is_greater_or_equal_approx(p_start_time, 0) && Animation::is_greater_or_equal_approx(p_end_time, 0) && Animation::is_greater_or_equal_approx(p_start_time, p_end_time), vformat("Start time %f is greater than end time %f.", p_start_time, p_end_time));
+ playback.current.start_time = p_start_time;
+ playback.current.end_time = p_end_time;
+ playback.current.pos = CLAMP(playback.current.pos, get_section_start_time(), get_section_end_time());
+}
+
+void AnimationPlayer::reset_section() {
+ playback.current.start_time = -1;
+ playback.current.end_time = -1;
+}
+
+double AnimationPlayer::get_section_start_time() const {
+ ERR_FAIL_NULL_V_MSG(playback.current.from, playback.current.start_time, "AnimationPlayer has no current animation.");
+ if (Animation::is_less_approx(playback.current.start_time, 0) || playback.current.start_time > playback.current.from->animation->get_length()) {
+ return 0;
+ }
+ return playback.current.start_time;
+}
+
+double AnimationPlayer::get_section_end_time() const {
+ ERR_FAIL_NULL_V_MSG(playback.current.from, playback.current.end_time, "AnimationPlayer has no current animation.");
+ if (Animation::is_less_approx(playback.current.end_time, 0) || playback.current.end_time > playback.current.from->animation->get_length()) {
+ return playback.current.from->animation->get_length();
+ }
+ return playback.current.end_time;
+}
+
+bool AnimationPlayer::has_section() const {
+ return Animation::is_greater_or_equal_approx(playback.current.start_time, 0) || Animation::is_greater_or_equal_approx(playback.current.end_time, 0);
+}
+
void AnimationPlayer::set_autoplay(const String &p_name) {
if (is_inside_tree() && !Engine::get_singleton()->is_editor_hint()) {
WARN_PRINT("Setting autoplay after the node has been added to the scene has no effect.");
@@ -665,13 +777,14 @@ void AnimationPlayer::_stop_internal(bool p_reset, bool p_keep_state) {
_clear_caches();
Playback &c = playback;
// c.blend.clear();
+ double start = get_section_start_time();
if (p_reset) {
c.blend.clear();
if (p_keep_state) {
- c.current.pos = 0;
+ c.current.pos = start;
} else {
is_stopping = true;
- seek_internal(0, true, true, true);
+ seek_internal(start, true, true, true);
is_stopping = false;
}
c.current.from = nullptr;
@@ -763,20 +876,6 @@ Tween::EaseType AnimationPlayer::get_auto_capture_ease_type() const {
return auto_capture_ease_type;
}
-#ifdef TOOLS_ENABLED
-void AnimationPlayer::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const {
- const String pf = p_function;
- if (p_idx == 0 && (pf == "play" || pf == "play_backwards" || pf == "has_animation" || pf == "queue")) {
- List<StringName> al;
- get_animation_list(&al);
- for (const StringName &name : al) {
- r_options->push_back(String(name).quote());
- }
- }
- AnimationMixer::get_argument_options(p_function, p_idx, r_options);
-}
-#endif
-
void AnimationPlayer::_animation_removed(const StringName &p_name, const StringName &p_library) {
AnimationMixer::_animation_removed(p_name, p_library);
@@ -863,7 +962,11 @@ void AnimationPlayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_auto_capture_ease_type"), &AnimationPlayer::get_auto_capture_ease_type);
ClassDB::bind_method(D_METHOD("play", "name", "custom_blend", "custom_speed", "from_end"), &AnimationPlayer::play, DEFVAL(StringName()), DEFVAL(-1), DEFVAL(1.0), DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("play_section_with_markers", "name", "start_marker", "end_marker", "custom_blend", "custom_speed", "from_end"), &AnimationPlayer::play_section_with_markers, DEFVAL(StringName()), DEFVAL(StringName()), DEFVAL(StringName()), DEFVAL(-1), DEFVAL(1.0), DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("play_section", "name", "start_time", "end_time", "custom_blend", "custom_speed", "from_end"), &AnimationPlayer::play_section, DEFVAL(StringName()), DEFVAL(-1), DEFVAL(-1), DEFVAL(-1), DEFVAL(1.0), DEFVAL(false));
ClassDB::bind_method(D_METHOD("play_backwards", "name", "custom_blend"), &AnimationPlayer::play_backwards, DEFVAL(StringName()), DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("play_section_with_markers_backwards", "name", "start_marker", "end_marker", "custom_blend"), &AnimationPlayer::play_section_with_markers_backwards, DEFVAL(StringName()), DEFVAL(StringName()), DEFVAL(StringName()), DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("play_section_backwards", "name", "start_time", "end_time", "custom_blend"), &AnimationPlayer::play_section_backwards, DEFVAL(StringName()), DEFVAL(-1), DEFVAL(-1), DEFVAL(-1));
ClassDB::bind_method(D_METHOD("play_with_capture", "name", "duration", "custom_blend", "custom_speed", "from_end", "trans_type", "ease_type"), &AnimationPlayer::play_with_capture, DEFVAL(StringName()), DEFVAL(-1.0), DEFVAL(-1), DEFVAL(1.0), DEFVAL(false), DEFVAL(Tween::TRANS_LINEAR), DEFVAL(Tween::EASE_IN));
ClassDB::bind_method(D_METHOD("pause"), &AnimationPlayer::pause);
ClassDB::bind_method(D_METHOD("stop", "keep_state"), &AnimationPlayer::stop, DEFVAL(false));
@@ -893,6 +996,14 @@ void AnimationPlayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_current_animation_position"), &AnimationPlayer::get_current_animation_position);
ClassDB::bind_method(D_METHOD("get_current_animation_length"), &AnimationPlayer::get_current_animation_length);
+ ClassDB::bind_method(D_METHOD("set_section_with_markers", "start_marker", "end_marker"), &AnimationPlayer::set_section_with_markers, DEFVAL(StringName()), DEFVAL(StringName()));
+ ClassDB::bind_method(D_METHOD("set_section", "start_time", "end_time"), &AnimationPlayer::set_section, DEFVAL(-1), DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("reset_section"), &AnimationPlayer::reset_section);
+
+ ClassDB::bind_method(D_METHOD("get_section_start_time"), &AnimationPlayer::get_section_start_time);
+ ClassDB::bind_method(D_METHOD("get_section_end_time"), &AnimationPlayer::get_section_end_time);
+ ClassDB::bind_method(D_METHOD("has_section"), &AnimationPlayer::has_section);
+
ClassDB::bind_method(D_METHOD("seek", "seconds", "update", "update_only"), &AnimationPlayer::seek, DEFVAL(false), DEFVAL(false));
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "current_animation", PROPERTY_HINT_ENUM, "", PROPERTY_USAGE_EDITOR), "set_current_animation", "get_current_animation");
diff --git a/scene/animation/animation_player.h b/scene/animation/animation_player.h
index e05a2c9935..3223e2522d 100644
--- a/scene/animation/animation_player.h
+++ b/scene/animation/animation_player.h
@@ -68,6 +68,8 @@ private:
AnimationData *from = nullptr;
double pos = 0.0;
float speed_scale = 1.0;
+ double start_time = 0.0;
+ double end_time = 0.0;
};
struct Blend {
@@ -177,7 +179,11 @@ public:
Tween::EaseType get_auto_capture_ease_type() const;
void play(const StringName &p_name = StringName(), double p_custom_blend = -1, float p_custom_scale = 1.0, bool p_from_end = false);
+ void play_section_with_markers(const StringName &p_name = StringName(), const StringName &p_start_marker = StringName(), const StringName &p_end_marker = StringName(), double p_custom_blend = -1, float p_custom_scale = 1.0, bool p_from_end = false);
+ void play_section(const StringName &p_name = StringName(), double p_start_time = -1, double p_end_time = -1, double p_custom_blend = -1, float p_custom_scale = 1.0, bool p_from_end = false);
void play_backwards(const StringName &p_name = StringName(), double p_custom_blend = -1);
+ void play_section_with_markers_backwards(const StringName &p_name = StringName(), const StringName &p_start_marker = StringName(), const StringName &p_end_marker = StringName(), double p_custom_blend = -1);
+ void play_section_backwards(const StringName &p_name = StringName(), double p_start_time = -1, double p_end_time = -1, double p_custom_blend = -1);
void play_with_capture(const StringName &p_name = StringName(), double p_duration = -1.0, double p_custom_blend = -1, float p_custom_scale = 1.0, bool p_from_end = false, Tween::TransitionType p_trans_type = Tween::TRANS_LINEAR, Tween::EaseType p_ease_type = Tween::EASE_IN);
void queue(const StringName &p_name);
Vector<String> get_queue();
@@ -207,9 +213,13 @@ public:
double get_current_animation_position() const;
double get_current_animation_length() const;
-#ifdef TOOLS_ENABLED
- void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override;
-#endif
+ void set_section_with_markers(const StringName &p_start_marker = StringName(), const StringName &p_end_marker = StringName());
+ void set_section(double p_start_time = -1, double p_end_time = -1);
+ void reset_section();
+
+ double get_section_start_time() const;
+ double get_section_end_time() const;
+ bool has_section() const;
virtual void advance(double p_time) override;
diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp
index 72299f788d..34f5095493 100644
--- a/scene/gui/base_button.cpp
+++ b/scene/gui/base_button.cpp
@@ -32,6 +32,7 @@
#include "core/config/project_settings.h"
#include "core/os/keyboard.h"
+#include "scene/gui/label.h"
#include "scene/main/window.h"
void BaseButton::_unpress_group() {
@@ -390,16 +391,31 @@ void BaseButton::shortcut_input(const Ref<InputEvent> &p_event) {
}
}
-String BaseButton::get_tooltip(const Point2 &p_pos) const {
- String tooltip = Control::get_tooltip(p_pos);
- if (shortcut_in_tooltip && shortcut.is_valid() && shortcut->has_valid_event()) {
- String text = shortcut->get_name() + " (" + shortcut->get_as_text() + ")";
- if (!tooltip.is_empty() && shortcut->get_name().nocasecmp_to(tooltip) != 0) {
- text += "\n" + atr(tooltip);
- }
- tooltip = text;
+Control *BaseButton::make_custom_tooltip(const String &p_text) const {
+ Control *control = Control::make_custom_tooltip(p_text);
+ if (control) {
+ return control;
+ }
+ if (!shortcut_in_tooltip || shortcut.is_null() || !shortcut->has_valid_event()) {
+ return nullptr; // Use the default tooltip label.
}
- return tooltip;
+
+ String text = atr(shortcut->get_name()) + " (" + shortcut->get_as_text() + ")";
+ if (!p_text.is_empty() && shortcut->get_name().nocasecmp_to(p_text) != 0) {
+ text += "\n" + atr(p_text);
+ }
+
+ // Make a label similar to the default tooltip label.
+ // Auto translation is disabled because we already did that manually above.
+ //
+ // We can't customize the tooltip text by overriding `get_tooltip()`
+ // because otherwise user-defined `_make_custom_tooltip()` would receive
+ // the translated and annotated text.
+ Label *label = memnew(Label(text));
+ label->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
+ label->set_theme_type_variation(SNAME("TooltipLabel"));
+
+ return label;
}
void BaseButton::set_button_group(const Ref<ButtonGroup> &p_group) {
diff --git a/scene/gui/base_button.h b/scene/gui/base_button.h
index a8d5cee44c..8405acb21d 100644
--- a/scene/gui/base_button.h
+++ b/scene/gui/base_button.h
@@ -134,7 +134,7 @@ public:
void set_shortcut(const Ref<Shortcut> &p_shortcut);
Ref<Shortcut> get_shortcut() const;
- virtual String get_tooltip(const Point2 &p_pos) const override;
+ virtual Control *make_custom_tooltip(const String &p_text) const override;
void set_button_group(const Ref<ButtonGroup> &p_group);
Ref<ButtonGroup> get_button_group() const;
diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp
index dd344121e1..0a5f2ec6c7 100644
--- a/scene/gui/button.cpp
+++ b/scene/gui/button.cpp
@@ -30,9 +30,7 @@
#include "button.h"
-#include "core/string/translation.h"
#include "scene/theme/theme_db.h"
-#include "servers/rendering_server.h"
Size2 Button::get_minimum_size() const {
Ref<Texture2D> _icon = icon;
@@ -437,6 +435,9 @@ void Button::_notification(int p_what) {
text_buf->set_alignment(align_rtl_checked);
float text_buf_width = Math::ceil(MAX(1.0f, drawable_size_remained.width)); // The space's width filled by the text_buf.
+ if (autowrap_mode != TextServer::AUTOWRAP_OFF && !Math::is_equal_approx(text_buf_width, text_buf->get_width())) {
+ update_minimum_size();
+ }
text_buf->set_width(text_buf_width);
Point2 text_ofs;
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index cecddebe88..c1d197ea9b 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -33,12 +33,8 @@
#include "container.h"
#include "core/config/project_settings.h"
#include "core/math/geometry_2d.h"
-#include "core/os/keyboard.h"
#include "core/os/os.h"
-#include "core/string/print_string.h"
#include "core/string/translation_server.h"
-#include "scene/gui/label.h"
-#include "scene/gui/panel.h"
#include "scene/main/canvas_layer.h"
#include "scene/main/window.h"
#include "scene/theme/theme_db.h"
@@ -3161,6 +3157,16 @@ bool Control::is_auto_translating() const {
}
#endif
+void Control::set_tooltip_auto_translate_mode(AutoTranslateMode p_mode) {
+ ERR_MAIN_THREAD_GUARD;
+ data.tooltip_auto_translate_mode = p_mode;
+}
+
+Node::AutoTranslateMode Control::get_tooltip_auto_translate_mode() const {
+ ERR_READ_THREAD_GUARD_V(AUTO_TRANSLATE_MODE_INHERIT);
+ return data.tooltip_auto_translate_mode;
+}
+
// Extra properties.
void Control::set_tooltip_text(const String &p_hint) {
@@ -3510,6 +3516,8 @@ void Control::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_v_grow_direction", "direction"), &Control::set_v_grow_direction);
ClassDB::bind_method(D_METHOD("get_v_grow_direction"), &Control::get_v_grow_direction);
+ ClassDB::bind_method(D_METHOD("set_tooltip_auto_translate_mode", "mode"), &Control::set_tooltip_auto_translate_mode);
+ ClassDB::bind_method(D_METHOD("get_tooltip_auto_translate_mode"), &Control::get_tooltip_auto_translate_mode);
ClassDB::bind_method(D_METHOD("set_tooltip_text", "hint"), &Control::set_tooltip_text);
ClassDB::bind_method(D_METHOD("get_tooltip_text"), &Control::get_tooltip_text);
ClassDB::bind_method(D_METHOD("get_tooltip", "at_position"), &Control::get_tooltip, DEFVAL(Point2()));
@@ -3617,6 +3625,7 @@ void Control::_bind_methods() {
ADD_GROUP("Tooltip", "tooltip_");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "tooltip_text", PROPERTY_HINT_MULTILINE_TEXT), "set_tooltip_text", "get_tooltip_text");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "tooltip_auto_translate_mode", PROPERTY_HINT_ENUM, "Inherit,Always,Disabled"), "set_tooltip_auto_translate_mode", "get_tooltip_auto_translate_mode");
ADD_GROUP("Focus", "focus_");
ADD_PROPERTYI(PropertyInfo(Variant::NODE_PATH, "focus_neighbor_left", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Control"), "set_focus_neighbor", "get_focus_neighbor", SIDE_LEFT);
diff --git a/scene/gui/control.h b/scene/gui/control.h
index 2655b14562..6e1621ce58 100644
--- a/scene/gui/control.h
+++ b/scene/gui/control.h
@@ -33,7 +33,6 @@
#include "core/math/transform_2d.h"
#include "core/object/gdvirtual.gen.inc"
-#include "core/templates/rid.h"
#include "scene/main/canvas_item.h"
#include "scene/main/timer.h"
#include "scene/resources/theme.h"
@@ -262,6 +261,7 @@ private:
// Extra properties.
String tooltip;
+ AutoTranslateMode tooltip_auto_translate_mode = AUTO_TRANSLATE_MODE_INHERIT;
} data;
@@ -634,6 +634,9 @@ public:
bool is_auto_translating() const;
#endif
+ void set_tooltip_auto_translate_mode(AutoTranslateMode p_mode);
+ AutoTranslateMode get_tooltip_auto_translate_mode() const;
+
// Extra properties.
void set_tooltip_text(const String &text);
diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp
index 11a6411e65..cf8815679f 100644
--- a/scene/gui/graph_edit.cpp
+++ b/scene/gui/graph_edit.cpp
@@ -1999,6 +1999,9 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
} else if (p_ev->is_action("ui_copy", true)) {
emit_signal(SNAME("copy_nodes_request"));
accept_event();
+ } else if (p_ev->is_action("ui_cut", true)) {
+ emit_signal(SNAME("cut_nodes_request"));
+ accept_event();
} else if (p_ev->is_action("ui_paste", true)) {
emit_signal(SNAME("paste_nodes_request"));
accept_event();
@@ -2735,6 +2738,7 @@ void GraphEdit::_bind_methods() {
ADD_SIGNAL(MethodInfo("connection_drag_ended"));
ADD_SIGNAL(MethodInfo("copy_nodes_request"));
+ ADD_SIGNAL(MethodInfo("cut_nodes_request"));
ADD_SIGNAL(MethodInfo("paste_nodes_request"));
ADD_SIGNAL(MethodInfo("duplicate_nodes_request"));
ADD_SIGNAL(MethodInfo("delete_nodes_request", PropertyInfo(Variant::ARRAY, "nodes", PROPERTY_HINT_ARRAY_TYPE, "StringName")));
diff --git a/scene/main/http_request.cpp b/scene/main/http_request.cpp
index 3469b806a6..8526611093 100644
--- a/scene/main/http_request.cpp
+++ b/scene/main/http_request.cpp
@@ -49,7 +49,8 @@ Error HTTPRequest::_parse_url(const String &p_url) {
redirections = 0;
String scheme;
- Error err = p_url.parse_url(scheme, url, port, request_string);
+ String fragment;
+ Error err = p_url.parse_url(scheme, url, port, request_string, fragment);
ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Error parsing URL: '%s'.", p_url));
if (scheme == "https://") {
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index ba69f8cc45..169a5dcb01 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -1400,7 +1400,7 @@ String Viewport::_gui_get_tooltip(Control *p_control, const Vector2 &p_pos, Cont
String tooltip;
while (p_control) {
- tooltip = p_control->atr(p_control->get_tooltip(pos));
+ tooltip = p_control->get_tooltip(pos);
// Temporary solution for PopupMenus.
PopupMenu *menu = Object::cast_to<PopupMenu>(this);
@@ -1483,6 +1483,7 @@ void Viewport::_gui_show_tooltip() {
}
base_tooltip->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
+ base_tooltip->set_auto_translate_mode(tooltip_owner->get_tooltip_auto_translate_mode());
panel->set_transient(true);
panel->set_flag(Window::FLAG_NO_FOCUS, true);
diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp
index 9abc6a02d2..57a4e35f7a 100644
--- a/scene/resources/animation.cpp
+++ b/scene/resources/animation.cpp
@@ -63,6 +63,23 @@ bool Animation::_set(const StringName &p_name, const Variant &p_value) {
}
compression.enabled = true;
return true;
+ } else if (prop_name == SNAME("markers")) {
+ Array markers = p_value;
+ for (const Dictionary marker : markers) {
+ ERR_FAIL_COND_V(!marker.has("name"), false);
+ ERR_FAIL_COND_V(!marker.has("time"), false);
+ StringName marker_name = marker["name"];
+ double time = marker["time"];
+ _marker_insert(time, marker_names, MarkerKey(time, marker_name));
+ marker_times.insert(marker_name, time);
+ Color color = Color(1, 1, 1);
+ if (marker.has("color")) {
+ color = marker["color"];
+ }
+ marker_colors.insert(marker_name, color);
+ }
+
+ return true;
} else if (prop_name.begins_with("tracks/")) {
int track = prop_name.get_slicec('/', 1).to_int();
String what = prop_name.get_slicec('/', 2);
@@ -470,6 +487,18 @@ bool Animation::_get(const StringName &p_name, Variant &r_ret) const {
r_ret = comp;
return true;
+ } else if (prop_name == SNAME("markers")) {
+ Array markers;
+
+ for (HashMap<StringName, double>::ConstIterator E = marker_times.begin(); E; ++E) {
+ Dictionary d;
+ d["name"] = E->key;
+ d["time"] = E->value;
+ d["color"] = marker_colors[E->key];
+ markers.push_back(d);
+ }
+
+ r_ret = markers;
} else if (prop_name == "length") {
r_ret = length;
} else if (prop_name == "loop_mode") {
@@ -839,6 +868,7 @@ void Animation::_get_property_list(List<PropertyInfo> *p_list) const {
if (compression.enabled) {
p_list->push_back(PropertyInfo(Variant::DICTIONARY, "_compression", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
}
+ p_list->push_back(PropertyInfo(Variant::ARRAY, "markers", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
for (int i = 0; i < tracks.size(); i++) {
p_list->push_back(PropertyInfo(Variant::STRING, "tracks/" + itos(i) + "/type", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
p_list->push_back(PropertyInfo(Variant::BOOL, "tracks/" + itos(i) + "/imported", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL));
@@ -1087,6 +1117,27 @@ int Animation::_insert(double p_time, T &p_keys, const V &p_value) {
return -1;
}
+int Animation::_marker_insert(double p_time, Vector<MarkerKey> &p_keys, const MarkerKey &p_value) {
+ int idx = p_keys.size();
+
+ while (true) {
+ // Condition for replacement.
+ if (idx > 0 && Math::is_equal_approx((double)p_keys[idx - 1].time, p_time)) {
+ p_keys.write[idx - 1] = p_value;
+ return idx - 1;
+
+ // Condition for insert.
+ } else if (idx == 0 || p_keys[idx - 1].time < p_time) {
+ p_keys.insert(idx, p_value);
+ return idx;
+ }
+
+ idx--;
+ }
+
+ return -1;
+}
+
template <typename T>
void Animation::_clear(T &p_keys) {
p_keys.clear();
@@ -3163,6 +3214,90 @@ void Animation::track_get_key_indices_in_range(int p_track, double p_time, doubl
}
}
+void Animation::add_marker(const StringName &p_name, double p_time) {
+ int idx = _find(marker_names, p_time);
+
+ if (idx >= 0 && idx < marker_names.size() && Math::is_equal_approx(p_time, marker_names[idx].time)) {
+ marker_times.erase(marker_names[idx].name);
+ marker_colors.erase(marker_names[idx].name);
+ marker_names.write[idx].name = p_name;
+ marker_times.insert(p_name, p_time);
+ marker_colors.insert(p_name, Color(1, 1, 1));
+ } else {
+ _marker_insert(p_time, marker_names, MarkerKey(p_time, p_name));
+ marker_times.insert(p_name, p_time);
+ marker_colors.insert(p_name, Color(1, 1, 1));
+ }
+}
+
+void Animation::remove_marker(const StringName &p_name) {
+ HashMap<StringName, double>::Iterator E = marker_times.find(p_name);
+ ERR_FAIL_COND(!E);
+ int idx = _find(marker_names, E->value);
+ bool success = idx >= 0 && idx < marker_names.size() && Math::is_equal_approx(marker_names[idx].time, E->value);
+ ERR_FAIL_COND(!success);
+ marker_names.remove_at(idx);
+ marker_times.remove(E);
+ marker_colors.erase(p_name);
+}
+
+bool Animation::has_marker(const StringName &p_name) const {
+ return marker_times.has(p_name);
+}
+
+StringName Animation::get_marker_at_time(double p_time) const {
+ int idx = _find(marker_names, p_time);
+
+ if (idx >= 0 && idx < marker_names.size() && Math::is_equal_approx(marker_names[idx].time, p_time)) {
+ return marker_names[idx].name;
+ }
+
+ return StringName();
+}
+
+StringName Animation::get_next_marker(double p_time) const {
+ int idx = _find(marker_names, p_time);
+
+ if (idx >= -1 && idx < marker_names.size() - 1) {
+ // _find ensures that the time at idx is always the closest time to p_time that is also smaller to it.
+ // So we add 1 to get the next marker.
+ return marker_names[idx + 1].name;
+ }
+ return StringName();
+}
+
+StringName Animation::get_prev_marker(double p_time) const {
+ int idx = _find(marker_names, p_time);
+
+ if (idx >= 0 && idx < marker_names.size()) {
+ return marker_names[idx].name;
+ }
+ return StringName();
+}
+
+double Animation::get_marker_time(const StringName &p_name) const {
+ ERR_FAIL_COND_V(!marker_times.has(p_name), -1);
+ return marker_times.get(p_name);
+}
+
+PackedStringArray Animation::get_marker_names() const {
+ PackedStringArray names;
+ // We iterate on marker_names so the result is sorted by time.
+ for (const MarkerKey &marker_name : marker_names) {
+ names.push_back(marker_name.name);
+ }
+ return names;
+}
+
+Color Animation::get_marker_color(const StringName &p_name) const {
+ ERR_FAIL_COND_V(!marker_colors.has(p_name), Color());
+ return marker_colors[p_name];
+}
+
+void Animation::set_marker_color(const StringName &p_name, const Color &p_color) {
+ marker_colors[p_name] = p_color;
+}
+
Vector<Variant> Animation::method_track_get_params(int p_track, int p_key_idx) const {
ERR_FAIL_INDEX_V(p_track, tracks.size(), Vector<Variant>());
Track *t = tracks[p_track];
@@ -3894,6 +4029,17 @@ void Animation::_bind_methods() {
ClassDB::bind_method(D_METHOD("animation_track_set_key_animation", "track_idx", "key_idx", "animation"), &Animation::animation_track_set_key_animation);
ClassDB::bind_method(D_METHOD("animation_track_get_key_animation", "track_idx", "key_idx"), &Animation::animation_track_get_key_animation);
+ ClassDB::bind_method(D_METHOD("add_marker", "name", "time"), &Animation::add_marker);
+ ClassDB::bind_method(D_METHOD("remove_marker", "name"), &Animation::remove_marker);
+ ClassDB::bind_method(D_METHOD("has_marker", "name"), &Animation::has_marker);
+ ClassDB::bind_method(D_METHOD("get_marker_at_time", "time"), &Animation::get_marker_at_time);
+ ClassDB::bind_method(D_METHOD("get_next_marker", "time"), &Animation::get_next_marker);
+ ClassDB::bind_method(D_METHOD("get_prev_marker", "time"), &Animation::get_prev_marker);
+ ClassDB::bind_method(D_METHOD("get_marker_time", "name"), &Animation::get_marker_time);
+ ClassDB::bind_method(D_METHOD("get_marker_names"), &Animation::get_marker_names);
+ ClassDB::bind_method(D_METHOD("get_marker_color", "name"), &Animation::get_marker_color);
+ ClassDB::bind_method(D_METHOD("set_marker_color", "name", "color"), &Animation::set_marker_color);
+
ClassDB::bind_method(D_METHOD("set_length", "time_sec"), &Animation::set_length);
ClassDB::bind_method(D_METHOD("get_length"), &Animation::get_length);
@@ -3906,6 +4052,7 @@ void Animation::_bind_methods() {
ClassDB::bind_method(D_METHOD("clear"), &Animation::clear);
ClassDB::bind_method(D_METHOD("copy_track", "track_idx", "to_animation"), &Animation::copy_track);
+ ClassDB::bind_method(D_METHOD("optimize", "allowed_velocity_err", "allowed_angular_err", "precision"), &Animation::optimize, DEFVAL(0.01), DEFVAL(0.01), DEFVAL(3));
ClassDB::bind_method(D_METHOD("compress", "page_size", "fps", "split_tolerance"), &Animation::compress, DEFVAL(8192), DEFVAL(120), DEFVAL(4.0));
ClassDB::bind_method(D_METHOD("is_capture_included"), &Animation::is_capture_included);
diff --git a/scene/resources/animation.h b/scene/resources/animation.h
index 0c29790ea4..618dc9ca17 100644
--- a/scene/resources/animation.h
+++ b/scene/resources/animation.h
@@ -237,6 +237,20 @@ private:
}
};
+ /* Marker */
+
+ struct MarkerKey {
+ double time;
+ StringName name;
+ MarkerKey(double p_time, const StringName &p_name) :
+ time(p_time), name(p_name) {}
+ MarkerKey() = default;
+ };
+
+ Vector<MarkerKey> marker_names; // time -> name
+ HashMap<StringName, double> marker_times; // name -> time
+ HashMap<StringName, Color> marker_colors; // name -> color
+
Vector<Track *> tracks;
template <typename T>
@@ -245,6 +259,8 @@ private:
template <typename T, typename V>
int _insert(double p_time, T &p_keys, const V &p_value);
+ int _marker_insert(double p_time, Vector<MarkerKey> &p_keys, const MarkerKey &p_value);
+
template <typename K>
inline int _find(const Vector<K> &p_keys, double p_time, bool p_backward = false, bool p_limit = false) const;
@@ -501,6 +517,17 @@ public:
void track_get_key_indices_in_range(int p_track, double p_time, double p_delta, List<int> *p_indices, Animation::LoopedFlag p_looped_flag = Animation::LOOPED_FLAG_NONE) const;
+ void add_marker(const StringName &p_name, double p_time);
+ void remove_marker(const StringName &p_name);
+ bool has_marker(const StringName &p_name) const;
+ StringName get_marker_at_time(double p_time) const;
+ StringName get_next_marker(double p_time) const;
+ StringName get_prev_marker(double p_time) const;
+ double get_marker_time(const StringName &p_time) const;
+ PackedStringArray get_marker_names() const;
+ Color get_marker_color(const StringName &p_name) const;
+ void set_marker_color(const StringName &p_name, const Color &p_color);
+
void set_length(real_t p_length);
real_t get_length() const;
diff --git a/scene/resources/gradient_texture.cpp b/scene/resources/gradient_texture.cpp
index 6ec9422d2d..2b0e455efb 100644
--- a/scene/resources/gradient_texture.cpp
+++ b/scene/resources/gradient_texture.cpp
@@ -85,7 +85,7 @@ void GradientTexture1D::_queue_update() {
callable_mp(this, &GradientTexture1D::update_now).call_deferred();
}
-void GradientTexture1D::_update() {
+void GradientTexture1D::_update() const {
update_pending = false;
if (gradient.is_null()) {
@@ -172,14 +172,14 @@ RID GradientTexture1D::get_rid() const {
}
Ref<Image> GradientTexture1D::get_image() const {
- const_cast<GradientTexture1D *>(this)->update_now();
+ update_now();
if (!texture.is_valid()) {
return Ref<Image>();
}
return RenderingServer::get_singleton()->texture_2d_get(texture);
}
-void GradientTexture1D::update_now() {
+void GradientTexture1D::update_now() const {
if (update_pending) {
_update();
}
@@ -225,7 +225,7 @@ void GradientTexture2D::_queue_update() {
callable_mp(this, &GradientTexture2D::update_now).call_deferred();
}
-void GradientTexture2D::_update() {
+void GradientTexture2D::_update() const {
update_pending = false;
if (gradient.is_null()) {
@@ -405,14 +405,14 @@ RID GradientTexture2D::get_rid() const {
}
Ref<Image> GradientTexture2D::get_image() const {
- const_cast<GradientTexture2D *>(this)->update_now();
+ update_now();
if (!texture.is_valid()) {
return Ref<Image>();
}
return RenderingServer::get_singleton()->texture_2d_get(texture);
}
-void GradientTexture2D::update_now() {
+void GradientTexture2D::update_now() const {
if (update_pending) {
_update();
}
diff --git a/scene/resources/gradient_texture.h b/scene/resources/gradient_texture.h
index 761e8d995b..764e5e6645 100644
--- a/scene/resources/gradient_texture.h
+++ b/scene/resources/gradient_texture.h
@@ -38,13 +38,13 @@ class GradientTexture1D : public Texture2D {
private:
Ref<Gradient> gradient;
- bool update_pending = false;
+ mutable bool update_pending = false;
mutable RID texture;
int width = 256;
bool use_hdr = false;
void _queue_update();
- void _update();
+ void _update() const;
protected:
static void _bind_methods();
@@ -64,7 +64,7 @@ public:
virtual bool has_alpha() const override { return true; }
virtual Ref<Image> get_image() const override;
- void update_now();
+ void update_now() const;
GradientTexture1D();
virtual ~GradientTexture1D();
@@ -102,9 +102,9 @@ private:
float _get_gradient_offset_at(int x, int y) const;
- bool update_pending = false;
+ mutable bool update_pending = false;
void _queue_update();
- void _update();
+ void _update() const;
protected:
static void _bind_methods();
@@ -134,7 +134,7 @@ public:
virtual RID get_rid() const override;
virtual bool has_alpha() const override { return true; }
virtual Ref<Image> get_image() const override;
- void update_now();
+ void update_now() const;
GradientTexture2D();
virtual ~GradientTexture2D();
diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp
index 4ee3802b08..9bd3f2edce 100644
--- a/scene/resources/material.cpp
+++ b/scene/resources/material.cpp
@@ -113,12 +113,12 @@ void Material::inspect_native_shader_code() {
RID Material::get_shader_rid() const {
RID ret;
- GDVIRTUAL_REQUIRED_CALL(_get_shader_rid, ret);
+ GDVIRTUAL_CALL(_get_shader_rid, ret);
return ret;
}
Shader::Mode Material::get_shader_mode() const {
Shader::Mode ret = Shader::MODE_MAX;
- GDVIRTUAL_REQUIRED_CALL(_get_shader_mode, ret);
+ GDVIRTUAL_CALL(_get_shader_mode, ret);
return ret;
}
diff --git a/scene/resources/material.h b/scene/resources/material.h
index 50a774e961..25c4450682 100644
--- a/scene/resources/material.h
+++ b/scene/resources/material.h
@@ -66,8 +66,8 @@ protected:
void _mark_initialized(const Callable &p_add_to_dirty_list, const Callable &p_update_shader);
bool _is_initialized() { return init_state == INIT_STATE_READY; }
- GDVIRTUAL0RC(RID, _get_shader_rid)
- GDVIRTUAL0RC(Shader::Mode, _get_shader_mode)
+ GDVIRTUAL0RC_REQUIRED(RID, _get_shader_rid)
+ GDVIRTUAL0RC_REQUIRED(Shader::Mode, _get_shader_mode)
GDVIRTUAL0RC(bool, _can_do_next_pass)
GDVIRTUAL0RC(bool, _can_use_render_priority)
public:
diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp
index 22e2e9138f..4d1d733f8b 100644
--- a/scene/resources/mesh.cpp
+++ b/scene/resources/mesh.cpp
@@ -205,81 +205,81 @@ Mesh::ConvexDecompositionFunc Mesh::convex_decomposition_function = nullptr;
int Mesh::get_surface_count() const {
int ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_get_surface_count, ret);
+ GDVIRTUAL_CALL(_get_surface_count, ret);
return ret;
}
int Mesh::surface_get_array_len(int p_idx) const {
int ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_surface_get_array_len, p_idx, ret);
+ GDVIRTUAL_CALL(_surface_get_array_len, p_idx, ret);
return ret;
}
int Mesh::surface_get_array_index_len(int p_idx) const {
int ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_surface_get_array_index_len, p_idx, ret);
+ GDVIRTUAL_CALL(_surface_get_array_index_len, p_idx, ret);
return ret;
}
Array Mesh::surface_get_arrays(int p_surface) const {
Array ret;
- GDVIRTUAL_REQUIRED_CALL(_surface_get_arrays, p_surface, ret);
+ GDVIRTUAL_CALL(_surface_get_arrays, p_surface, ret);
return ret;
}
TypedArray<Array> Mesh::surface_get_blend_shape_arrays(int p_surface) const {
TypedArray<Array> ret;
- GDVIRTUAL_REQUIRED_CALL(_surface_get_blend_shape_arrays, p_surface, ret);
+ GDVIRTUAL_CALL(_surface_get_blend_shape_arrays, p_surface, ret);
return ret;
}
Dictionary Mesh::surface_get_lods(int p_surface) const {
Dictionary ret;
- GDVIRTUAL_REQUIRED_CALL(_surface_get_lods, p_surface, ret);
+ GDVIRTUAL_CALL(_surface_get_lods, p_surface, ret);
return ret;
}
BitField<Mesh::ArrayFormat> Mesh::surface_get_format(int p_idx) const {
uint32_t ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_surface_get_format, p_idx, ret);
+ GDVIRTUAL_CALL(_surface_get_format, p_idx, ret);
return ret;
}
Mesh::PrimitiveType Mesh::surface_get_primitive_type(int p_idx) const {
uint32_t ret = PRIMITIVE_MAX;
- GDVIRTUAL_REQUIRED_CALL(_surface_get_primitive_type, p_idx, ret);
+ GDVIRTUAL_CALL(_surface_get_primitive_type, p_idx, ret);
return (Mesh::PrimitiveType)ret;
}
void Mesh::surface_set_material(int p_idx, const Ref<Material> &p_material) {
- GDVIRTUAL_REQUIRED_CALL(_surface_set_material, p_idx, p_material);
+ GDVIRTUAL_CALL(_surface_set_material, p_idx, p_material);
}
Ref<Material> Mesh::surface_get_material(int p_idx) const {
Ref<Material> ret;
- GDVIRTUAL_REQUIRED_CALL(_surface_get_material, p_idx, ret);
+ GDVIRTUAL_CALL(_surface_get_material, p_idx, ret);
return ret;
}
int Mesh::get_blend_shape_count() const {
int ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_get_blend_shape_count, ret);
+ GDVIRTUAL_CALL(_get_blend_shape_count, ret);
return ret;
}
StringName Mesh::get_blend_shape_name(int p_index) const {
StringName ret;
- GDVIRTUAL_REQUIRED_CALL(_get_blend_shape_name, p_index, ret);
+ GDVIRTUAL_CALL(_get_blend_shape_name, p_index, ret);
return ret;
}
void Mesh::set_blend_shape_name(int p_index, const StringName &p_name) {
- GDVIRTUAL_REQUIRED_CALL(_set_blend_shape_name, p_index, p_name);
+ GDVIRTUAL_CALL(_set_blend_shape_name, p_index, p_name);
}
AABB Mesh::get_aabb() const {
AABB ret;
- GDVIRTUAL_REQUIRED_CALL(_get_aabb, ret);
+ GDVIRTUAL_CALL(_get_aabb, ret);
return ret;
}
diff --git a/scene/resources/mesh.h b/scene/resources/mesh.h
index 13fd986e81..068bfb6708 100644
--- a/scene/resources/mesh.h
+++ b/scene/resources/mesh.h
@@ -68,20 +68,20 @@ public:
protected:
static void _bind_methods();
- GDVIRTUAL0RC(int, _get_surface_count)
- GDVIRTUAL1RC(int, _surface_get_array_len, int)
- GDVIRTUAL1RC(int, _surface_get_array_index_len, int)
- GDVIRTUAL1RC(Array, _surface_get_arrays, int)
- GDVIRTUAL1RC(TypedArray<Array>, _surface_get_blend_shape_arrays, int)
- GDVIRTUAL1RC(Dictionary, _surface_get_lods, int)
- GDVIRTUAL1RC(uint32_t, _surface_get_format, int)
- GDVIRTUAL1RC(uint32_t, _surface_get_primitive_type, int)
- GDVIRTUAL2(_surface_set_material, int, Ref<Material>)
- GDVIRTUAL1RC(Ref<Material>, _surface_get_material, int)
- GDVIRTUAL0RC(int, _get_blend_shape_count)
- GDVIRTUAL1RC(StringName, _get_blend_shape_name, int)
- GDVIRTUAL2(_set_blend_shape_name, int, StringName)
- GDVIRTUAL0RC(AABB, _get_aabb)
+ GDVIRTUAL0RC_REQUIRED(int, _get_surface_count)
+ GDVIRTUAL1RC_REQUIRED(int, _surface_get_array_len, int)
+ GDVIRTUAL1RC_REQUIRED(int, _surface_get_array_index_len, int)
+ GDVIRTUAL1RC_REQUIRED(Array, _surface_get_arrays, int)
+ GDVIRTUAL1RC_REQUIRED(TypedArray<Array>, _surface_get_blend_shape_arrays, int)
+ GDVIRTUAL1RC_REQUIRED(Dictionary, _surface_get_lods, int)
+ GDVIRTUAL1RC_REQUIRED(uint32_t, _surface_get_format, int)
+ GDVIRTUAL1RC_REQUIRED(uint32_t, _surface_get_primitive_type, int)
+ GDVIRTUAL2_REQUIRED(_surface_set_material, int, Ref<Material>)
+ GDVIRTUAL1RC_REQUIRED(Ref<Material>, _surface_get_material, int)
+ GDVIRTUAL0RC_REQUIRED(int, _get_blend_shape_count)
+ GDVIRTUAL1RC_REQUIRED(StringName, _get_blend_shape_name, int)
+ GDVIRTUAL2_REQUIRED(_set_blend_shape_name, int, StringName)
+ GDVIRTUAL0RC_REQUIRED(AABB, _get_aabb)
public:
enum {
diff --git a/scene/resources/style_box.cpp b/scene/resources/style_box.cpp
index f87bf1ee05..1203c21a1b 100644
--- a/scene/resources/style_box.cpp
+++ b/scene/resources/style_box.cpp
@@ -90,7 +90,7 @@ Point2 StyleBox::get_offset() const {
}
void StyleBox::draw(RID p_canvas_item, const Rect2 &p_rect) const {
- GDVIRTUAL_REQUIRED_CALL(_draw, p_canvas_item, p_rect);
+ GDVIRTUAL_CALL(_draw, p_canvas_item, p_rect);
}
Rect2 StyleBox::get_draw_rect(const Rect2 &p_rect) const {
diff --git a/scene/resources/style_box.h b/scene/resources/style_box.h
index 3d3a059d0b..e09b271c47 100644
--- a/scene/resources/style_box.h
+++ b/scene/resources/style_box.h
@@ -48,7 +48,7 @@ protected:
static void _bind_methods();
virtual float get_style_margin(Side p_side) const { return 0; }
- GDVIRTUAL2C(_draw, RID, Rect2)
+ GDVIRTUAL2C_REQUIRED(_draw, RID, Rect2)
GDVIRTUAL1RC(Rect2, _get_draw_rect, Rect2)
GDVIRTUAL0RC(Size2, _get_minimum_size)
GDVIRTUAL2RC(bool, _test_mask, Point2, Rect2)
diff --git a/scene/resources/syntax_highlighter.cpp b/scene/resources/syntax_highlighter.cpp
index 6da4215031..da90ba1ef2 100644
--- a/scene/resources/syntax_highlighter.cpp
+++ b/scene/resources/syntax_highlighter.cpp
@@ -442,7 +442,6 @@ Color CodeHighlighter::get_keyword_color(const String &p_keyword) const {
}
void CodeHighlighter::set_keyword_colors(const Dictionary p_keywords) {
- keywords.clear();
keywords = p_keywords;
clear_highlighting_cache();
}
@@ -476,7 +475,6 @@ Color CodeHighlighter::get_member_keyword_color(const String &p_member_keyword)
}
void CodeHighlighter::set_member_keyword_colors(const Dictionary &p_member_keywords) {
- member_keywords.clear();
member_keywords = p_member_keywords;
clear_highlighting_cache();
}
diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp
index 0efaad61fe..7713181a4b 100644
--- a/scene/resources/texture.cpp
+++ b/scene/resources/texture.cpp
@@ -34,13 +34,13 @@
int Texture2D::get_width() const {
int ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_get_width, ret);
+ GDVIRTUAL_CALL(_get_width, ret);
return ret;
}
int Texture2D::get_height() const {
int ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_get_height, ret);
+ GDVIRTUAL_CALL(_get_height, ret);
return ret;
}
@@ -133,37 +133,37 @@ TypedArray<Image> Texture3D::_get_datai() const {
Image::Format Texture3D::get_format() const {
Image::Format ret = Image::FORMAT_MAX;
- GDVIRTUAL_REQUIRED_CALL(_get_format, ret);
+ GDVIRTUAL_CALL(_get_format, ret);
return ret;
}
int Texture3D::get_width() const {
int ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_get_width, ret);
+ GDVIRTUAL_CALL(_get_width, ret);
return ret;
}
int Texture3D::get_height() const {
int ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_get_height, ret);
+ GDVIRTUAL_CALL(_get_height, ret);
return ret;
}
int Texture3D::get_depth() const {
int ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_get_depth, ret);
+ GDVIRTUAL_CALL(_get_depth, ret);
return ret;
}
bool Texture3D::has_mipmaps() const {
bool ret = false;
- GDVIRTUAL_REQUIRED_CALL(_has_mipmaps, ret);
+ GDVIRTUAL_CALL(_has_mipmaps, ret);
return ret;
}
Vector<Ref<Image>> Texture3D::get_data() const {
TypedArray<Image> ret;
- GDVIRTUAL_REQUIRED_CALL(_get_data, ret);
+ GDVIRTUAL_CALL(_get_data, ret);
Vector<Ref<Image>> data;
data.resize(ret.size());
for (int i = 0; i < data.size(); i++) {
@@ -198,43 +198,43 @@ Ref<Resource> Texture3D::create_placeholder() const {
Image::Format TextureLayered::get_format() const {
Image::Format ret = Image::FORMAT_MAX;
- GDVIRTUAL_REQUIRED_CALL(_get_format, ret);
+ GDVIRTUAL_CALL(_get_format, ret);
return ret;
}
TextureLayered::LayeredType TextureLayered::get_layered_type() const {
uint32_t ret = LAYERED_TYPE_2D_ARRAY;
- GDVIRTUAL_REQUIRED_CALL(_get_layered_type, ret);
+ GDVIRTUAL_CALL(_get_layered_type, ret);
return (LayeredType)ret;
}
int TextureLayered::get_width() const {
int ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_get_width, ret);
+ GDVIRTUAL_CALL(_get_width, ret);
return ret;
}
int TextureLayered::get_height() const {
int ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_get_height, ret);
+ GDVIRTUAL_CALL(_get_height, ret);
return ret;
}
int TextureLayered::get_layers() const {
int ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_get_layers, ret);
+ GDVIRTUAL_CALL(_get_layers, ret);
return ret;
}
bool TextureLayered::has_mipmaps() const {
bool ret = false;
- GDVIRTUAL_REQUIRED_CALL(_has_mipmaps, ret);
+ GDVIRTUAL_CALL(_has_mipmaps, ret);
return ret;
}
Ref<Image> TextureLayered::get_layer_data(int p_layer) const {
Ref<Image> ret;
- GDVIRTUAL_REQUIRED_CALL(_get_layer_data, p_layer, ret);
+ GDVIRTUAL_CALL(_get_layer_data, p_layer, ret);
return ret;
}
diff --git a/scene/resources/texture.h b/scene/resources/texture.h
index e7840804bf..cdad884e71 100644
--- a/scene/resources/texture.h
+++ b/scene/resources/texture.h
@@ -57,8 +57,8 @@ class Texture2D : public Texture {
protected:
static void _bind_methods();
- GDVIRTUAL0RC(int, _get_width)
- GDVIRTUAL0RC(int, _get_height)
+ GDVIRTUAL0RC_REQUIRED(int, _get_width)
+ GDVIRTUAL0RC_REQUIRED(int, _get_height)
GDVIRTUAL2RC(bool, _is_pixel_opaque, int, int)
GDVIRTUAL0RC(bool, _has_alpha)
@@ -93,13 +93,13 @@ class TextureLayered : public Texture {
protected:
static void _bind_methods();
- GDVIRTUAL0RC(Image::Format, _get_format)
- GDVIRTUAL0RC(uint32_t, _get_layered_type)
- GDVIRTUAL0RC(int, _get_width)
- GDVIRTUAL0RC(int, _get_height)
- GDVIRTUAL0RC(int, _get_layers)
- GDVIRTUAL0RC(bool, _has_mipmaps)
- GDVIRTUAL1RC(Ref<Image>, _get_layer_data, int)
+ GDVIRTUAL0RC_REQUIRED(Image::Format, _get_format)
+ GDVIRTUAL0RC_REQUIRED(uint32_t, _get_layered_type)
+ GDVIRTUAL0RC_REQUIRED(int, _get_width)
+ GDVIRTUAL0RC_REQUIRED(int, _get_height)
+ GDVIRTUAL0RC_REQUIRED(int, _get_layers)
+ GDVIRTUAL0RC_REQUIRED(bool, _has_mipmaps)
+ GDVIRTUAL1RC_REQUIRED(Ref<Image>, _get_layer_data, int)
public:
enum LayeredType {
LAYERED_TYPE_2D_ARRAY,
@@ -128,12 +128,12 @@ protected:
TypedArray<Image> _get_datai() const;
- GDVIRTUAL0RC(Image::Format, _get_format)
- GDVIRTUAL0RC(int, _get_width)
- GDVIRTUAL0RC(int, _get_height)
- GDVIRTUAL0RC(int, _get_depth)
- GDVIRTUAL0RC(bool, _has_mipmaps)
- GDVIRTUAL0RC(TypedArray<Image>, _get_data)
+ GDVIRTUAL0RC_REQUIRED(Image::Format, _get_format)
+ GDVIRTUAL0RC_REQUIRED(int, _get_width)
+ GDVIRTUAL0RC_REQUIRED(int, _get_height)
+ GDVIRTUAL0RC_REQUIRED(int, _get_depth)
+ GDVIRTUAL0RC_REQUIRED(bool, _has_mipmaps)
+ GDVIRTUAL0RC_REQUIRED(TypedArray<Image>, _get_data)
public:
virtual Image::Format get_format() const;
virtual int get_width() const;
diff --git a/scene/resources/video_stream.cpp b/scene/resources/video_stream.cpp
index 3d31fe0491..1fcc1821dc 100644
--- a/scene/resources/video_stream.cpp
+++ b/scene/resources/video_stream.cpp
@@ -119,7 +119,7 @@ Ref<Texture2D> VideoStreamPlayback::get_texture() const {
}
void VideoStreamPlayback::update(double p_delta) {
- GDVIRTUAL_REQUIRED_CALL(_update, p_delta);
+ GDVIRTUAL_CALL(_update, p_delta);
}
void VideoStreamPlayback::set_mix_callback(AudioMixCallback p_callback, void *p_userdata) {
diff --git a/scene/resources/video_stream.h b/scene/resources/video_stream.h
index dc54f4b769..3843cc99c4 100644
--- a/scene/resources/video_stream.h
+++ b/scene/resources/video_stream.h
@@ -56,7 +56,7 @@ protected:
GDVIRTUAL1(_seek, double);
GDVIRTUAL1(_set_audio_track, int);
GDVIRTUAL0RC(Ref<Texture2D>, _get_texture);
- GDVIRTUAL1(_update, double);
+ GDVIRTUAL1_REQUIRED(_update, double);
GDVIRTUAL0RC(int, _get_channels);
GDVIRTUAL0RC(int, _get_mix_rate);
diff --git a/scene/scene_string_names.cpp b/scene/scene_string_names.cpp
index f8a0336b37..140e588291 100644
--- a/scene/scene_string_names.cpp
+++ b/scene/scene_string_names.cpp
@@ -141,5 +141,6 @@ SceneStringNames::SceneStringNames() {
confirmed = StaticCString::create("confirmed");
text_changed = StaticCString::create("text_changed");
+ text_submitted = StaticCString::create("text_submitted");
value_changed = StaticCString::create("value_changed");
}
diff --git a/scene/scene_string_names.h b/scene/scene_string_names.h
index 381a161ad5..fc22be33b2 100644
--- a/scene/scene_string_names.h
+++ b/scene/scene_string_names.h
@@ -154,6 +154,7 @@ public:
StringName confirmed;
StringName text_changed;
+ StringName text_submitted;
StringName value_changed;
};
diff --git a/servers/audio/audio_effect.cpp b/servers/audio/audio_effect.cpp
index 28a957cf6f..ac334eb764 100644
--- a/servers/audio/audio_effect.cpp
+++ b/servers/audio/audio_effect.cpp
@@ -31,7 +31,7 @@
#include "audio_effect.h"
void AudioEffectInstance::process(const AudioFrame *p_src_frames, AudioFrame *p_dst_frames, int p_frame_count) {
- GDVIRTUAL_REQUIRED_CALL(_process, p_src_frames, p_dst_frames, p_frame_count);
+ GDVIRTUAL_CALL(_process, p_src_frames, p_dst_frames, p_frame_count);
}
bool AudioEffectInstance::process_silence() const {
bool ret = false;
@@ -48,7 +48,7 @@ void AudioEffectInstance::_bind_methods() {
Ref<AudioEffectInstance> AudioEffect::instantiate() {
Ref<AudioEffectInstance> ret;
- GDVIRTUAL_REQUIRED_CALL(_instantiate, ret);
+ GDVIRTUAL_CALL(_instantiate, ret);
return ret;
}
void AudioEffect::_bind_methods() {
diff --git a/servers/audio/audio_effect.h b/servers/audio/audio_effect.h
index 9952246c20..cef5bfec5e 100644
--- a/servers/audio/audio_effect.h
+++ b/servers/audio/audio_effect.h
@@ -40,7 +40,7 @@ class AudioEffectInstance : public RefCounted {
GDCLASS(AudioEffectInstance, RefCounted);
protected:
- GDVIRTUAL3(_process, GDExtensionConstPtr<AudioFrame>, GDExtensionPtr<AudioFrame>, int)
+ GDVIRTUAL3_REQUIRED(_process, GDExtensionConstPtr<AudioFrame>, GDExtensionPtr<AudioFrame>, int)
GDVIRTUAL0RC(bool, _process_silence)
static void _bind_methods();
@@ -53,7 +53,7 @@ class AudioEffect : public Resource {
GDCLASS(AudioEffect, Resource);
protected:
- GDVIRTUAL0R(Ref<AudioEffectInstance>, _instantiate)
+ GDVIRTUAL0R_REQUIRED(Ref<AudioEffectInstance>, _instantiate)
static void _bind_methods();
public:
diff --git a/servers/audio/audio_stream.cpp b/servers/audio/audio_stream.cpp
index ece088a694..1886ebe1ac 100644
--- a/servers/audio/audio_stream.cpp
+++ b/servers/audio/audio_stream.cpp
@@ -72,7 +72,7 @@ void AudioStreamPlayback::seek(double p_time) {
int AudioStreamPlayback::mix(AudioFrame *p_buffer, float p_rate_scale, int p_frames) {
int ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_mix, p_buffer, p_rate_scale, p_frames, ret);
+ GDVIRTUAL_CALL(_mix, p_buffer, p_rate_scale, p_frames, ret);
return ret;
}
@@ -132,12 +132,12 @@ void AudioStreamPlaybackResampled::begin_resample() {
int AudioStreamPlaybackResampled::_mix_internal(AudioFrame *p_buffer, int p_frames) {
int ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_mix_resampled, p_buffer, p_frames, ret);
+ GDVIRTUAL_CALL(_mix_resampled, p_buffer, p_frames, ret);
return ret;
}
float AudioStreamPlaybackResampled::get_stream_sampling_rate() {
float ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_get_stream_sampling_rate, ret);
+ GDVIRTUAL_CALL(_get_stream_sampling_rate, ret);
return ret;
}
diff --git a/servers/audio/audio_stream.h b/servers/audio/audio_stream.h
index 7f2b68a796..3feaa53630 100644
--- a/servers/audio/audio_stream.h
+++ b/servers/audio/audio_stream.h
@@ -89,7 +89,7 @@ protected:
GDVIRTUAL0RC(int, _get_loop_count)
GDVIRTUAL0RC(double, _get_playback_position)
GDVIRTUAL1(_seek, double)
- GDVIRTUAL3R(int, _mix, GDExtensionPtr<AudioFrame>, float, int)
+ GDVIRTUAL3R_REQUIRED(int, _mix, GDExtensionPtr<AudioFrame>, float, int)
GDVIRTUAL0(_tag_used_streams)
GDVIRTUAL2(_set_parameter, const StringName &, const Variant &)
GDVIRTUAL1RC(Variant, _get_parameter, const StringName &)
@@ -141,8 +141,8 @@ protected:
virtual int _mix_internal(AudioFrame *p_buffer, int p_frames);
virtual float get_stream_sampling_rate();
- GDVIRTUAL2R(int, _mix_resampled, GDExtensionPtr<AudioFrame>, int)
- GDVIRTUAL0RC(float, _get_stream_sampling_rate)
+ GDVIRTUAL2R_REQUIRED(int, _mix_resampled, GDExtensionPtr<AudioFrame>, int)
+ GDVIRTUAL0RC_REQUIRED(float, _get_stream_sampling_rate)
static void _bind_methods();
diff --git a/servers/camera/camera_feed.cpp b/servers/camera/camera_feed.cpp
index 8f6a40481d..4021d9564b 100644
--- a/servers/camera/camera_feed.cpp
+++ b/servers/camera/camera_feed.cpp
@@ -33,26 +33,23 @@
#include "servers/rendering_server.h"
void CameraFeed::_bind_methods() {
- // The setters prefixed with _ are only exposed so we can have feeds through GDExtension!
- // They should not be called by the end user.
-
ClassDB::bind_method(D_METHOD("get_id"), &CameraFeed::get_id);
ClassDB::bind_method(D_METHOD("is_active"), &CameraFeed::is_active);
ClassDB::bind_method(D_METHOD("set_active", "active"), &CameraFeed::set_active);
ClassDB::bind_method(D_METHOD("get_name"), &CameraFeed::get_name);
- ClassDB::bind_method(D_METHOD("_set_name", "name"), &CameraFeed::set_name);
+ ClassDB::bind_method(D_METHOD("set_name", "name"), &CameraFeed::set_name);
ClassDB::bind_method(D_METHOD("get_position"), &CameraFeed::get_position);
- ClassDB::bind_method(D_METHOD("_set_position", "position"), &CameraFeed::set_position);
+ ClassDB::bind_method(D_METHOD("set_position", "position"), &CameraFeed::set_position);
// Note, for transform some feeds may override what the user sets (such as ARKit)
ClassDB::bind_method(D_METHOD("get_transform"), &CameraFeed::get_transform);
ClassDB::bind_method(D_METHOD("set_transform", "transform"), &CameraFeed::set_transform);
- ClassDB::bind_method(D_METHOD("_set_RGB_img", "rgb_img"), &CameraFeed::set_RGB_img);
- ClassDB::bind_method(D_METHOD("_set_YCbCr_img", "ycbcr_img"), &CameraFeed::set_YCbCr_img);
+ ClassDB::bind_method(D_METHOD("set_rgb_image", "rgb_image"), &CameraFeed::set_rgb_image);
+ ClassDB::bind_method(D_METHOD("set_ycbcr_image", "ycbcr_image"), &CameraFeed::set_ycbcr_image);
ClassDB::bind_method(D_METHOD("get_datatype"), &CameraFeed::get_datatype);
@@ -175,7 +172,7 @@ CameraFeed::~CameraFeed() {
RenderingServer::get_singleton()->free(texture[CameraServer::FEED_CBCR_IMAGE]);
}
-void CameraFeed::set_RGB_img(const Ref<Image> &p_rgb_img) {
+void CameraFeed::set_rgb_image(const Ref<Image> &p_rgb_img) {
ERR_FAIL_COND(p_rgb_img.is_null());
if (active) {
int new_width = p_rgb_img->get_width();
@@ -198,7 +195,7 @@ void CameraFeed::set_RGB_img(const Ref<Image> &p_rgb_img) {
}
}
-void CameraFeed::set_YCbCr_img(const Ref<Image> &p_ycbcr_img) {
+void CameraFeed::set_ycbcr_image(const Ref<Image> &p_ycbcr_img) {
ERR_FAIL_COND(p_ycbcr_img.is_null());
if (active) {
int new_width = p_ycbcr_img->get_width();
@@ -221,7 +218,7 @@ void CameraFeed::set_YCbCr_img(const Ref<Image> &p_ycbcr_img) {
}
}
-void CameraFeed::set_YCbCr_imgs(const Ref<Image> &p_y_img, const Ref<Image> &p_cbcr_img) {
+void CameraFeed::set_ycbcr_images(const Ref<Image> &p_y_img, const Ref<Image> &p_cbcr_img) {
ERR_FAIL_COND(p_y_img.is_null());
ERR_FAIL_COND(p_cbcr_img.is_null());
if (active) {
diff --git a/servers/camera/camera_feed.h b/servers/camera/camera_feed.h
index 5d1f54be07..492a909239 100644
--- a/servers/camera/camera_feed.h
+++ b/servers/camera/camera_feed.h
@@ -110,9 +110,9 @@ public:
virtual ~CameraFeed();
FeedDataType get_datatype() const;
- void set_RGB_img(const Ref<Image> &p_rgb_img);
- void set_YCbCr_img(const Ref<Image> &p_ycbcr_img);
- void set_YCbCr_imgs(const Ref<Image> &p_y_img, const Ref<Image> &p_cbcr_img);
+ void set_rgb_image(const Ref<Image> &p_rgb_img);
+ void set_ycbcr_image(const Ref<Image> &p_ycbcr_img);
+ void set_ycbcr_images(const Ref<Image> &p_y_img, const Ref<Image> &p_cbcr_img);
virtual bool set_format(int p_index, const Dictionary &p_parameters);
virtual Array get_formats() const;
diff --git a/servers/extensions/physics_server_2d_extension.h b/servers/extensions/physics_server_2d_extension.h
index 6e0277a7c6..7bbd84ddf4 100644
--- a/servers/extensions/physics_server_2d_extension.h
+++ b/servers/extensions/physics_server_2d_extension.h
@@ -127,53 +127,53 @@ protected:
static void _bind_methods();
bool is_body_excluded_from_query(const RID &p_body) const;
- GDVIRTUAL7R(bool, _intersect_ray, const Vector2 &, const Vector2 &, uint32_t, bool, bool, bool, GDExtensionPtr<PhysicsServer2DExtensionRayResult>)
- GDVIRTUAL7R(int, _intersect_point, const Vector2 &, ObjectID, uint32_t, bool, bool, GDExtensionPtr<PhysicsServer2DExtensionShapeResult>, int)
- GDVIRTUAL9R(int, _intersect_shape, RID, const Transform2D &, const Vector2 &, real_t, uint32_t, bool, bool, GDExtensionPtr<PhysicsServer2DExtensionShapeResult>, int)
- GDVIRTUAL9R(bool, _cast_motion, RID, const Transform2D &, const Vector2 &, real_t, uint32_t, bool, bool, GDExtensionPtr<real_t>, GDExtensionPtr<real_t>)
- GDVIRTUAL10R(bool, _collide_shape, RID, const Transform2D &, const Vector2 &, real_t, uint32_t, bool, bool, GDExtensionPtr<Vector2>, int, GDExtensionPtr<int>)
- GDVIRTUAL8R(bool, _rest_info, RID, const Transform2D &, const Vector2 &, real_t, uint32_t, bool, bool, GDExtensionPtr<PhysicsServer2DExtensionShapeRestInfo>)
+ GDVIRTUAL7R_REQUIRED(bool, _intersect_ray, const Vector2 &, const Vector2 &, uint32_t, bool, bool, bool, GDExtensionPtr<PhysicsServer2DExtensionRayResult>)
+ GDVIRTUAL7R_REQUIRED(int, _intersect_point, const Vector2 &, ObjectID, uint32_t, bool, bool, GDExtensionPtr<PhysicsServer2DExtensionShapeResult>, int)
+ GDVIRTUAL9R_REQUIRED(int, _intersect_shape, RID, const Transform2D &, const Vector2 &, real_t, uint32_t, bool, bool, GDExtensionPtr<PhysicsServer2DExtensionShapeResult>, int)
+ GDVIRTUAL9R_REQUIRED(bool, _cast_motion, RID, const Transform2D &, const Vector2 &, real_t, uint32_t, bool, bool, GDExtensionPtr<real_t>, GDExtensionPtr<real_t>)
+ GDVIRTUAL10R_REQUIRED(bool, _collide_shape, RID, const Transform2D &, const Vector2 &, real_t, uint32_t, bool, bool, GDExtensionPtr<Vector2>, int, GDExtensionPtr<int>)
+ GDVIRTUAL8R_REQUIRED(bool, _rest_info, RID, const Transform2D &, const Vector2 &, real_t, uint32_t, bool, bool, GDExtensionPtr<PhysicsServer2DExtensionShapeRestInfo>)
public:
virtual bool intersect_ray(const RayParameters &p_parameters, RayResult &r_result) override {
exclude = &p_parameters.exclude;
bool ret = false;
- GDVIRTUAL_REQUIRED_CALL(_intersect_ray, p_parameters.from, p_parameters.to, p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas, p_parameters.hit_from_inside, &r_result, ret);
+ GDVIRTUAL_CALL(_intersect_ray, p_parameters.from, p_parameters.to, p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas, p_parameters.hit_from_inside, &r_result, ret);
exclude = nullptr;
return ret;
}
virtual int intersect_point(const PointParameters &p_parameters, ShapeResult *r_results, int p_result_max) override {
exclude = &p_parameters.exclude;
int ret = false;
- GDVIRTUAL_REQUIRED_CALL(_intersect_point, p_parameters.position, p_parameters.canvas_instance_id, p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas, r_results, p_result_max, ret);
+ GDVIRTUAL_CALL(_intersect_point, p_parameters.position, p_parameters.canvas_instance_id, p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas, r_results, p_result_max, ret);
exclude = nullptr;
return ret;
}
virtual int intersect_shape(const ShapeParameters &p_parameters, ShapeResult *r_results, int p_result_max) override {
exclude = &p_parameters.exclude;
int ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_intersect_shape, p_parameters.shape_rid, p_parameters.transform, p_parameters.motion, p_parameters.margin, p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas, r_results, p_result_max, ret);
+ GDVIRTUAL_CALL(_intersect_shape, p_parameters.shape_rid, p_parameters.transform, p_parameters.motion, p_parameters.margin, p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas, r_results, p_result_max, ret);
exclude = nullptr;
return ret;
}
virtual bool cast_motion(const ShapeParameters &p_parameters, real_t &p_closest_safe, real_t &p_closest_unsafe) override {
exclude = &p_parameters.exclude;
bool ret = false;
- GDVIRTUAL_REQUIRED_CALL(_cast_motion, p_parameters.shape_rid, p_parameters.transform, p_parameters.motion, p_parameters.margin, p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas, &p_closest_safe, &p_closest_unsafe, ret);
+ GDVIRTUAL_CALL(_cast_motion, p_parameters.shape_rid, p_parameters.transform, p_parameters.motion, p_parameters.margin, p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas, &p_closest_safe, &p_closest_unsafe, ret);
exclude = nullptr;
return ret;
}
virtual bool collide_shape(const ShapeParameters &p_parameters, Vector2 *r_results, int p_result_max, int &r_result_count) override {
exclude = &p_parameters.exclude;
bool ret = false;
- GDVIRTUAL_REQUIRED_CALL(_collide_shape, p_parameters.shape_rid, p_parameters.transform, p_parameters.motion, p_parameters.margin, p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas, r_results, p_result_max, &r_result_count, ret);
+ GDVIRTUAL_CALL(_collide_shape, p_parameters.shape_rid, p_parameters.transform, p_parameters.motion, p_parameters.margin, p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas, r_results, p_result_max, &r_result_count, ret);
exclude = nullptr;
return ret;
}
virtual bool rest_info(const ShapeParameters &p_parameters, ShapeRestInfo *r_info) override {
exclude = &p_parameters.exclude;
bool ret = false;
- GDVIRTUAL_REQUIRED_CALL(_rest_info, p_parameters.shape_rid, p_parameters.transform, p_parameters.motion, p_parameters.margin, p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas, r_info, ret);
+ GDVIRTUAL_CALL(_rest_info, p_parameters.shape_rid, p_parameters.transform, p_parameters.motion, p_parameters.margin, p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas, r_info, ret);
exclude = nullptr;
return ret;
}
@@ -191,9 +191,9 @@ class PhysicsServer2DExtension : public PhysicsServer2D {
protected:
static void _bind_methods();
- GDVIRTUAL9R(bool, _shape_collide, RID, const Transform2D &, const Vector2 &, RID, const Transform2D &, const Vector2 &, GDExtensionPtr<Vector2>, int, GDExtensionPtr<int>)
+ GDVIRTUAL9R_REQUIRED(bool, _shape_collide, RID, const Transform2D &, const Vector2 &, RID, const Transform2D &, const Vector2 &, GDExtensionPtr<Vector2>, int, GDExtensionPtr<int>)
- GDVIRTUAL8R(bool, _body_collide_shape, RID, int, RID, const Transform2D &, const Vector2 &, GDExtensionPtr<Vector2>, int, GDExtensionPtr<int>)
+ GDVIRTUAL8R_REQUIRED(bool, _body_collide_shape, RID, int, RID, const Transform2D &, const Vector2 &, GDExtensionPtr<Vector2>, int, GDExtensionPtr<int>)
public:
// The warning is valid, but unavoidable. If the function is not overridden it will error anyway.
@@ -218,7 +218,7 @@ public:
virtual bool shape_collide(RID p_shape_A, const Transform2D &p_xform_A, const Vector2 &p_motion_A, RID p_shape_B, const Transform2D &p_xform_B, const Vector2 &p_motion_B, Vector2 *r_results, int p_result_max, int &r_result_count) override {
bool ret = false;
- GDVIRTUAL_REQUIRED_CALL(_shape_collide, p_shape_A, p_xform_A, p_motion_A, p_shape_B, p_xform_B, p_motion_B, r_results, p_result_max, &r_result_count, ret);
+ GDVIRTUAL_CALL(_shape_collide, p_shape_A, p_xform_A, p_motion_A, p_shape_B, p_xform_B, p_motion_B, r_results, p_result_max, &r_result_count, ret);
return ret;
}
@@ -354,11 +354,11 @@ public:
EXBIND2(body_add_collision_exception, RID, RID)
EXBIND2(body_remove_collision_exception, RID, RID)
- GDVIRTUAL1RC(TypedArray<RID>, _body_get_collision_exceptions, RID)
+ GDVIRTUAL1RC_REQUIRED(TypedArray<RID>, _body_get_collision_exceptions, RID)
void body_get_collision_exceptions(RID p_body, List<RID> *p_exceptions) override {
TypedArray<RID> ret;
- GDVIRTUAL_REQUIRED_CALL(_body_get_collision_exceptions, p_body, ret);
+ GDVIRTUAL_CALL(_body_get_collision_exceptions, p_body, ret);
for (int i = 0; i < ret.size(); i++) {
p_exceptions->push_back(ret[i]);
}
@@ -378,7 +378,7 @@ public:
virtual bool body_collide_shape(RID p_body, int p_body_shape, RID p_shape, const Transform2D &p_shape_xform, const Vector2 &p_motion, Vector2 *r_results, int p_result_max, int &r_result_count) override {
bool ret = false;
- GDVIRTUAL_REQUIRED_CALL(_body_collide_shape, p_body, p_body_shape, p_shape, p_shape_xform, p_motion, r_results, p_result_max, &r_result_count, ret);
+ GDVIRTUAL_CALL(_body_collide_shape, p_body, p_body_shape, p_shape, p_shape_xform, p_motion, r_results, p_result_max, &r_result_count, ret);
return ret;
}
@@ -386,7 +386,7 @@ public:
EXBIND1R(PhysicsDirectBodyState2D *, body_get_direct_state, RID)
- GDVIRTUAL7RC(bool, _body_test_motion, RID, const Transform2D &, const Vector2 &, real_t, bool, bool, GDExtensionPtr<PhysicsServer2DExtensionMotionResult>)
+ GDVIRTUAL7RC_REQUIRED(bool, _body_test_motion, RID, const Transform2D &, const Vector2 &, real_t, bool, bool, GDExtensionPtr<PhysicsServer2DExtensionMotionResult>)
thread_local static const HashSet<RID> *exclude_bodies;
thread_local static const HashSet<ObjectID> *exclude_objects;
@@ -398,7 +398,7 @@ public:
bool ret = false;
exclude_bodies = &p_parameters.exclude_bodies;
exclude_objects = &p_parameters.exclude_objects;
- GDVIRTUAL_REQUIRED_CALL(_body_test_motion, p_body, p_parameters.from, p_parameters.motion, p_parameters.margin, p_parameters.collide_separation_ray, p_parameters.recovery_as_collision, r_result, ret);
+ GDVIRTUAL_CALL(_body_test_motion, p_body, p_parameters.from, p_parameters.motion, p_parameters.margin, p_parameters.collide_separation_ray, p_parameters.recovery_as_collision, r_result, ret);
exclude_bodies = nullptr;
exclude_objects = nullptr;
return ret;
@@ -432,9 +432,9 @@ public:
/* MISC */
- GDVIRTUAL1(_free_rid, RID)
+ GDVIRTUAL1_REQUIRED(_free_rid, RID)
virtual void free(RID p_rid) override {
- GDVIRTUAL_REQUIRED_CALL(_free_rid, p_rid);
+ GDVIRTUAL_CALL(_free_rid, p_rid);
}
EXBIND1(set_active, bool)
diff --git a/servers/extensions/physics_server_3d_extension.h b/servers/extensions/physics_server_3d_extension.h
index eb8b627245..b808b80b93 100644
--- a/servers/extensions/physics_server_3d_extension.h
+++ b/servers/extensions/physics_server_3d_extension.h
@@ -129,61 +129,61 @@ protected:
static void _bind_methods();
bool is_body_excluded_from_query(const RID &p_body) const;
- GDVIRTUAL9R(bool, _intersect_ray, const Vector3 &, const Vector3 &, uint32_t, bool, bool, bool, bool, bool, GDExtensionPtr<PhysicsServer3DExtensionRayResult>)
- GDVIRTUAL6R(int, _intersect_point, const Vector3 &, uint32_t, bool, bool, GDExtensionPtr<PhysicsServer3DExtensionShapeResult>, int)
- GDVIRTUAL9R(int, _intersect_shape, RID, const Transform3D &, const Vector3 &, real_t, uint32_t, bool, bool, GDExtensionPtr<PhysicsServer3DExtensionShapeResult>, int)
- GDVIRTUAL10R(bool, _cast_motion, RID, const Transform3D &, const Vector3 &, real_t, uint32_t, bool, bool, GDExtensionPtr<real_t>, GDExtensionPtr<real_t>, GDExtensionPtr<PhysicsServer3DExtensionShapeRestInfo>)
- GDVIRTUAL10R(bool, _collide_shape, RID, const Transform3D &, const Vector3 &, real_t, uint32_t, bool, bool, GDExtensionPtr<Vector3>, int, GDExtensionPtr<int>)
- GDVIRTUAL8R(bool, _rest_info, RID, const Transform3D &, const Vector3 &, real_t, uint32_t, bool, bool, GDExtensionPtr<PhysicsServer3DExtensionShapeRestInfo>)
- GDVIRTUAL2RC(Vector3, _get_closest_point_to_object_volume, RID, const Vector3 &)
+ GDVIRTUAL9R_REQUIRED(bool, _intersect_ray, const Vector3 &, const Vector3 &, uint32_t, bool, bool, bool, bool, bool, GDExtensionPtr<PhysicsServer3DExtensionRayResult>)
+ GDVIRTUAL6R_REQUIRED(int, _intersect_point, const Vector3 &, uint32_t, bool, bool, GDExtensionPtr<PhysicsServer3DExtensionShapeResult>, int)
+ GDVIRTUAL9R_REQUIRED(int, _intersect_shape, RID, const Transform3D &, const Vector3 &, real_t, uint32_t, bool, bool, GDExtensionPtr<PhysicsServer3DExtensionShapeResult>, int)
+ GDVIRTUAL10R_REQUIRED(bool, _cast_motion, RID, const Transform3D &, const Vector3 &, real_t, uint32_t, bool, bool, GDExtensionPtr<real_t>, GDExtensionPtr<real_t>, GDExtensionPtr<PhysicsServer3DExtensionShapeRestInfo>)
+ GDVIRTUAL10R_REQUIRED(bool, _collide_shape, RID, const Transform3D &, const Vector3 &, real_t, uint32_t, bool, bool, GDExtensionPtr<Vector3>, int, GDExtensionPtr<int>)
+ GDVIRTUAL8R_REQUIRED(bool, _rest_info, RID, const Transform3D &, const Vector3 &, real_t, uint32_t, bool, bool, GDExtensionPtr<PhysicsServer3DExtensionShapeRestInfo>)
+ GDVIRTUAL2RC_REQUIRED(Vector3, _get_closest_point_to_object_volume, RID, const Vector3 &)
public:
virtual bool intersect_ray(const RayParameters &p_parameters, RayResult &r_result) override {
exclude = &p_parameters.exclude;
bool ret = false;
- GDVIRTUAL_REQUIRED_CALL(_intersect_ray, p_parameters.from, p_parameters.to, p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas, p_parameters.hit_from_inside, p_parameters.hit_back_faces, p_parameters.pick_ray, &r_result, ret);
+ GDVIRTUAL_CALL(_intersect_ray, p_parameters.from, p_parameters.to, p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas, p_parameters.hit_from_inside, p_parameters.hit_back_faces, p_parameters.pick_ray, &r_result, ret);
exclude = nullptr;
return ret;
}
virtual int intersect_point(const PointParameters &p_parameters, ShapeResult *r_results, int p_result_max) override {
exclude = &p_parameters.exclude;
int ret = false;
- GDVIRTUAL_REQUIRED_CALL(_intersect_point, p_parameters.position, p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas, r_results, p_result_max, ret);
+ GDVIRTUAL_CALL(_intersect_point, p_parameters.position, p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas, r_results, p_result_max, ret);
exclude = nullptr;
return ret;
}
virtual int intersect_shape(const ShapeParameters &p_parameters, ShapeResult *r_results, int p_result_max) override {
exclude = &p_parameters.exclude;
int ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_intersect_shape, p_parameters.shape_rid, p_parameters.transform, p_parameters.motion, p_parameters.margin, p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas, r_results, p_result_max, ret);
+ GDVIRTUAL_CALL(_intersect_shape, p_parameters.shape_rid, p_parameters.transform, p_parameters.motion, p_parameters.margin, p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas, r_results, p_result_max, ret);
exclude = nullptr;
return ret;
}
virtual bool cast_motion(const ShapeParameters &p_parameters, real_t &p_closest_safe, real_t &p_closest_unsafe, ShapeRestInfo *r_info = nullptr) override {
exclude = &p_parameters.exclude;
bool ret = false;
- GDVIRTUAL_REQUIRED_CALL(_cast_motion, p_parameters.shape_rid, p_parameters.transform, p_parameters.motion, p_parameters.margin, p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas, &p_closest_safe, &p_closest_unsafe, r_info, ret);
+ GDVIRTUAL_CALL(_cast_motion, p_parameters.shape_rid, p_parameters.transform, p_parameters.motion, p_parameters.margin, p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas, &p_closest_safe, &p_closest_unsafe, r_info, ret);
exclude = nullptr;
return ret;
}
virtual bool collide_shape(const ShapeParameters &p_parameters, Vector3 *r_results, int p_result_max, int &r_result_count) override {
exclude = &p_parameters.exclude;
bool ret = false;
- GDVIRTUAL_REQUIRED_CALL(_collide_shape, p_parameters.shape_rid, p_parameters.transform, p_parameters.motion, p_parameters.margin, p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas, r_results, p_result_max, &r_result_count, ret);
+ GDVIRTUAL_CALL(_collide_shape, p_parameters.shape_rid, p_parameters.transform, p_parameters.motion, p_parameters.margin, p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas, r_results, p_result_max, &r_result_count, ret);
exclude = nullptr;
return ret;
}
virtual bool rest_info(const ShapeParameters &p_parameters, ShapeRestInfo *r_info) override {
exclude = &p_parameters.exclude;
bool ret = false;
- GDVIRTUAL_REQUIRED_CALL(_rest_info, p_parameters.shape_rid, p_parameters.transform, p_parameters.motion, p_parameters.margin, p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas, r_info, ret);
+ GDVIRTUAL_CALL(_rest_info, p_parameters.shape_rid, p_parameters.transform, p_parameters.motion, p_parameters.margin, p_parameters.collision_mask, p_parameters.collide_with_bodies, p_parameters.collide_with_areas, r_info, ret);
exclude = nullptr;
return ret;
}
virtual Vector3 get_closest_point_to_object_volume(RID p_object, const Vector3 p_point) const override {
Vector3 ret;
- GDVIRTUAL_REQUIRED_CALL(_get_closest_point_to_object_volume, p_object, p_point, ret);
+ GDVIRTUAL_CALL(_get_closest_point_to_object_volume, p_object, p_point, ret);
return ret;
}
@@ -359,11 +359,11 @@ public:
EXBIND2(body_add_collision_exception, RID, RID)
EXBIND2(body_remove_collision_exception, RID, RID)
- GDVIRTUAL1RC(TypedArray<RID>, _body_get_collision_exceptions, RID)
+ GDVIRTUAL1RC_REQUIRED(TypedArray<RID>, _body_get_collision_exceptions, RID)
void body_get_collision_exceptions(RID p_body, List<RID> *p_exceptions) override {
TypedArray<RID> ret;
- GDVIRTUAL_REQUIRED_CALL(_body_get_collision_exceptions, p_body, ret);
+ GDVIRTUAL_CALL(_body_get_collision_exceptions, p_body, ret);
for (int i = 0; i < ret.size(); i++) {
p_exceptions->push_back(ret[i]);
}
@@ -383,7 +383,7 @@ public:
EXBIND2(body_set_ray_pickable, RID, bool)
- GDVIRTUAL8RC(bool, _body_test_motion, RID, const Transform3D &, const Vector3 &, real_t, int, bool, bool, GDExtensionPtr<PhysicsServer3DExtensionMotionResult>)
+ GDVIRTUAL8RC_REQUIRED(bool, _body_test_motion, RID, const Transform3D &, const Vector3 &, real_t, int, bool, bool, GDExtensionPtr<PhysicsServer3DExtensionMotionResult>)
thread_local static const HashSet<RID> *exclude_bodies;
thread_local static const HashSet<ObjectID> *exclude_objects;
@@ -395,7 +395,7 @@ public:
bool ret = false;
exclude_bodies = &p_parameters.exclude_bodies;
exclude_objects = &p_parameters.exclude_objects;
- GDVIRTUAL_REQUIRED_CALL(_body_test_motion, p_body, p_parameters.from, p_parameters.motion, p_parameters.margin, p_parameters.max_collisions, p_parameters.collide_separation_ray, p_parameters.recovery_as_collision, r_result, ret);
+ GDVIRTUAL_CALL(_body_test_motion, p_body, p_parameters.from, p_parameters.motion, p_parameters.margin, p_parameters.max_collisions, p_parameters.collide_separation_ray, p_parameters.recovery_as_collision, r_result, ret);
exclude_bodies = nullptr;
exclude_objects = nullptr;
return ret;
@@ -423,11 +423,11 @@ public:
EXBIND2(soft_body_add_collision_exception, RID, RID)
EXBIND2(soft_body_remove_collision_exception, RID, RID)
- GDVIRTUAL1RC(TypedArray<RID>, _soft_body_get_collision_exceptions, RID)
+ GDVIRTUAL1RC_REQUIRED(TypedArray<RID>, _soft_body_get_collision_exceptions, RID)
void soft_body_get_collision_exceptions(RID p_soft_body, List<RID> *p_exceptions) override {
TypedArray<RID> ret;
- GDVIRTUAL_REQUIRED_CALL(_soft_body_get_collision_exceptions, p_soft_body, ret);
+ GDVIRTUAL_CALL(_soft_body_get_collision_exceptions, p_soft_body, ret);
for (int i = 0; i < ret.size(); i++) {
p_exceptions->push_back(ret[i]);
}
@@ -520,9 +520,9 @@ public:
/* MISC */
- GDVIRTUAL1(_free_rid, RID)
+ GDVIRTUAL1_REQUIRED(_free_rid, RID)
virtual void free(RID p_rid) override {
- GDVIRTUAL_REQUIRED_CALL(_free_rid, p_rid);
+ GDVIRTUAL_CALL(_free_rid, p_rid);
}
EXBIND1(set_active, bool)
diff --git a/servers/movie_writer/movie_writer.cpp b/servers/movie_writer/movie_writer.cpp
index aebed4b432..14e920d8cd 100644
--- a/servers/movie_writer/movie_writer.cpp
+++ b/servers/movie_writer/movie_writer.cpp
@@ -54,40 +54,40 @@ MovieWriter *MovieWriter::find_writer_for_file(const String &p_file) {
uint32_t MovieWriter::get_audio_mix_rate() const {
uint32_t ret = 48000;
- GDVIRTUAL_REQUIRED_CALL(_get_audio_mix_rate, ret);
+ GDVIRTUAL_CALL(_get_audio_mix_rate, ret);
return ret;
}
AudioServer::SpeakerMode MovieWriter::get_audio_speaker_mode() const {
AudioServer::SpeakerMode ret = AudioServer::SPEAKER_MODE_STEREO;
- GDVIRTUAL_REQUIRED_CALL(_get_audio_speaker_mode, ret);
+ GDVIRTUAL_CALL(_get_audio_speaker_mode, ret);
return ret;
}
Error MovieWriter::write_begin(const Size2i &p_movie_size, uint32_t p_fps, const String &p_base_path) {
Error ret = ERR_UNCONFIGURED;
- GDVIRTUAL_REQUIRED_CALL(_write_begin, p_movie_size, p_fps, p_base_path, ret);
+ GDVIRTUAL_CALL(_write_begin, p_movie_size, p_fps, p_base_path, ret);
return ret;
}
Error MovieWriter::write_frame(const Ref<Image> &p_image, const int32_t *p_audio_data) {
Error ret = ERR_UNCONFIGURED;
- GDVIRTUAL_REQUIRED_CALL(_write_frame, p_image, p_audio_data, ret);
+ GDVIRTUAL_CALL(_write_frame, p_image, p_audio_data, ret);
return ret;
}
void MovieWriter::write_end() {
- GDVIRTUAL_REQUIRED_CALL(_write_end);
+ GDVIRTUAL_CALL(_write_end);
}
bool MovieWriter::handles_file(const String &p_path) const {
bool ret = false;
- GDVIRTUAL_REQUIRED_CALL(_handles_file, p_path, ret);
+ GDVIRTUAL_CALL(_handles_file, p_path, ret);
return ret;
}
void MovieWriter::get_supported_extensions(List<String> *r_extensions) const {
Vector<String> exts;
- GDVIRTUAL_REQUIRED_CALL(_get_supported_extensions, exts);
+ GDVIRTUAL_CALL(_get_supported_extensions, exts);
for (int i = 0; i < exts.size(); i++) {
r_extensions->push_back(exts[i]);
}
diff --git a/servers/movie_writer/movie_writer.h b/servers/movie_writer/movie_writer.h
index 4757de203c..e1dc8ef8cf 100644
--- a/servers/movie_writer/movie_writer.h
+++ b/servers/movie_writer/movie_writer.h
@@ -63,15 +63,15 @@ protected:
virtual Error write_frame(const Ref<Image> &p_image, const int32_t *p_audio_data);
virtual void write_end();
- GDVIRTUAL0RC(uint32_t, _get_audio_mix_rate)
- GDVIRTUAL0RC(AudioServer::SpeakerMode, _get_audio_speaker_mode)
+ GDVIRTUAL0RC_REQUIRED(uint32_t, _get_audio_mix_rate)
+ GDVIRTUAL0RC_REQUIRED(AudioServer::SpeakerMode, _get_audio_speaker_mode)
- GDVIRTUAL1RC(bool, _handles_file, const String &)
- GDVIRTUAL0RC(Vector<String>, _get_supported_extensions)
+ GDVIRTUAL1RC_REQUIRED(bool, _handles_file, const String &)
+ GDVIRTUAL0RC_REQUIRED(Vector<String>, _get_supported_extensions)
- GDVIRTUAL3R(Error, _write_begin, const Size2i &, uint32_t, const String &)
- GDVIRTUAL2R(Error, _write_frame, const Ref<Image> &, GDExtensionConstPtr<int32_t>)
- GDVIRTUAL0(_write_end)
+ GDVIRTUAL3R_REQUIRED(Error, _write_begin, const Size2i &, uint32_t, const String &)
+ GDVIRTUAL2R_REQUIRED(Error, _write_frame, const Ref<Image> &, GDExtensionConstPtr<int32_t>)
+ GDVIRTUAL0_REQUIRED(_write_end)
static void _bind_methods();
diff --git a/servers/physics_server_3d.cpp b/servers/physics_server_3d.cpp
index ee48151863..312b67ada9 100644
--- a/servers/physics_server_3d.cpp
+++ b/servers/physics_server_3d.cpp
@@ -37,13 +37,13 @@
#include "core/variant/typed_array.h"
void PhysicsServer3DRenderingServerHandler::set_vertex(int p_vertex_id, const Vector3 &p_vertex) {
- GDVIRTUAL_REQUIRED_CALL(_set_vertex, p_vertex_id, p_vertex);
+ GDVIRTUAL_CALL(_set_vertex, p_vertex_id, p_vertex);
}
void PhysicsServer3DRenderingServerHandler::set_normal(int p_vertex_id, const Vector3 &p_normal) {
- GDVIRTUAL_REQUIRED_CALL(_set_normal, p_vertex_id, p_normal);
+ GDVIRTUAL_CALL(_set_normal, p_vertex_id, p_normal);
}
void PhysicsServer3DRenderingServerHandler::set_aabb(const AABB &p_aabb) {
- GDVIRTUAL_REQUIRED_CALL(_set_aabb, p_aabb);
+ GDVIRTUAL_CALL(_set_aabb, p_aabb);
}
void PhysicsServer3DRenderingServerHandler::_bind_methods() {
diff --git a/servers/physics_server_3d.h b/servers/physics_server_3d.h
index ea785fa03f..33def7cf8d 100644
--- a/servers/physics_server_3d.h
+++ b/servers/physics_server_3d.h
@@ -213,9 +213,9 @@ public:
class PhysicsServer3DRenderingServerHandler : public Object {
GDCLASS(PhysicsServer3DRenderingServerHandler, Object)
protected:
- GDVIRTUAL2(_set_vertex, int, const Vector3 &)
- GDVIRTUAL2(_set_normal, int, const Vector3 &)
- GDVIRTUAL1(_set_aabb, const AABB &)
+ GDVIRTUAL2_REQUIRED(_set_vertex, int, const Vector3 &)
+ GDVIRTUAL2_REQUIRED(_set_normal, int, const Vector3 &)
+ GDVIRTUAL1_REQUIRED(_set_aabb, const AABB &)
static void _bind_methods();
diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
index aca85ce497..307898232d 100644
--- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
+++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
@@ -4261,6 +4261,11 @@ RenderForwardClustered::RenderForwardClustered() {
defines += "\n#define SDFGI_OCT_SIZE " + itos(gi.sdfgi_get_lightprobe_octahedron_size()) + "\n";
defines += "\n#define MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS " + itos(MAX_DIRECTIONAL_LIGHTS) + "\n";
+ bool force_vertex_shading = GLOBAL_GET("rendering/shading/overrides/force_vertex_shading");
+ if (force_vertex_shading) {
+ defines += "\n#define USE_VERTEX_LIGHTING\n";
+ }
+
{
//lightmaps
scene_state.max_lightmaps = MAX_LIGHTMAPS;
diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp
index 6846c3f693..ee5b5ef1d8 100644
--- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp
+++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp
@@ -730,13 +730,20 @@ void SceneShaderForwardClustered::init(const String p_defines) {
actions.render_mode_defines["ambient_light_disabled"] = "#define AMBIENT_LIGHT_DISABLED\n";
actions.render_mode_defines["shadow_to_opacity"] = "#define USE_SHADOW_TO_OPACITY\n";
actions.render_mode_defines["unshaded"] = "#define MODE_UNSHADED\n";
+
+ bool force_vertex_shading = GLOBAL_GET("rendering/shading/overrides/force_vertex_shading");
+ if (!force_vertex_shading) {
+ // If forcing vertex shading, this will be defined already.
+ actions.render_mode_defines["vertex_lighting"] = "#define USE_VERTEX_LIGHTING\n";
+ }
+
actions.render_mode_defines["debug_shadow_splits"] = "#define DEBUG_DRAW_PSSM_SPLITS\n";
actions.render_mode_defines["fog_disabled"] = "#define FOG_DISABLED\n";
actions.base_texture_binding_index = 1;
actions.texture_layout_set = RenderForwardClustered::MATERIAL_UNIFORM_SET;
actions.base_uniform_string = "material.";
- actions.base_varying_index = 12;
+ actions.base_varying_index = 14;
actions.default_filter = ShaderLanguage::FILTER_LINEAR_MIPMAP;
actions.default_repeat = ShaderLanguage::REPEAT_ENABLE;
diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
index 8a02ec0eb5..fa8796fae6 100644
--- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
+++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
@@ -2800,6 +2800,11 @@ RenderForwardMobile::RenderForwardMobile() {
// defines += "\n#define SDFGI_OCT_SIZE " + itos(gi.sdfgi_get_lightprobe_octahedron_size()) + "\n";
defines += "\n#define MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS " + itos(MAX_DIRECTIONAL_LIGHTS) + "\n";
+ bool force_vertex_shading = GLOBAL_GET("rendering/shading/overrides/force_vertex_shading");
+ if (force_vertex_shading) {
+ defines += "\n#define USE_VERTEX_LIGHTING\n";
+ }
+
{
//lightmaps
scene_state.max_lightmaps = 2;
diff --git a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp
index 08982096c5..5dec4d8f21 100644
--- a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp
+++ b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp
@@ -633,6 +633,13 @@ void SceneShaderForwardMobile::init(const String p_defines) {
actions.render_mode_defines["ambient_light_disabled"] = "#define AMBIENT_LIGHT_DISABLED\n";
actions.render_mode_defines["shadow_to_opacity"] = "#define USE_SHADOW_TO_OPACITY\n";
actions.render_mode_defines["unshaded"] = "#define MODE_UNSHADED\n";
+
+ bool force_vertex_shading = GLOBAL_GET("rendering/shading/overrides/force_vertex_shading");
+ if (!force_vertex_shading) {
+ // If forcing vertex shading, this will be defined already.
+ actions.render_mode_defines["vertex_lighting"] = "#define USE_VERTEX_LIGHTING\n";
+ }
+
actions.render_mode_defines["debug_shadow_splits"] = "#define DEBUG_DRAW_PSSM_SPLITS\n";
actions.render_mode_defines["fog_disabled"] = "#define FOG_DISABLED\n";
diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
index 57497eb207..5441e28be0 100644
--- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
@@ -1974,6 +1974,12 @@ RendererCanvasRenderRD::RendererCanvasRenderRD() {
default_canvas_texture = texture_storage->canvas_texture_allocate();
texture_storage->canvas_texture_initialize(default_canvas_texture);
+ RendererRD::TextureStorage::CanvasTextureInfo info = RendererRD::TextureStorage::get_singleton()->canvas_texture_get_info(default_canvas_texture, default_filter, default_repeat, false, false);
+ default_texture_info.diffuse = info.diffuse;
+ default_texture_info.normal = info.normal;
+ default_texture_info.specular = info.specular;
+ default_texture_info.sampler = info.sampler;
+
state.shadow_texture_size = GLOBAL_GET("rendering/2d/shadow_atlas/size");
//create functions for shader and material
@@ -2219,7 +2225,7 @@ void RendererCanvasRenderRD::_render_batch_items(RenderTarget p_to_render_target
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, state.default_transforms_uniform_set, TRANSFORMS_UNIFORM_SET);
Item *current_clip = nullptr;
- state.current_tex_uniform_set = RID();
+ state.current_batch_uniform_set = RID();
for (uint32_t i = 0; i <= state.current_batch_index; i++) {
Batch *current_batch = &state.canvas_instance_batches[i];
@@ -2337,11 +2343,11 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
instance_data->world[i] = world[i];
}
- instance_data->flags = base_flags | r_current_batch->tex_flags; // Reset on each command for safety, keep canvas texture binding config.
+ instance_data->flags = base_flags | r_current_batch->tex_info.flags; // Reset on each command for safety, keep canvas texture binding config.
- instance_data->color_texture_pixel_size[0] = r_current_batch->tex_texpixel_size.width;
- instance_data->color_texture_pixel_size[1] = r_current_batch->tex_texpixel_size.height;
- instance_data->specular_shininess = r_current_batch->tex_specular_shininess;
+ instance_data->color_texture_pixel_size[0] = r_current_batch->tex_info.texpixel_size.width;
+ instance_data->color_texture_pixel_size[1] = r_current_batch->tex_info.texpixel_size.height;
+ instance_data->specular_shininess = r_current_batch->tex_info.specular_shininess;
return instance_data;
};
@@ -2373,10 +2379,10 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
bool has_msdf = bool(rect->flags & CANVAS_RECT_MSDF);
TextureState tex_state(rect->texture, texture_filter, texture_repeat, has_msdf, use_linear_colors);
- if (tex_state != r_current_batch->tex_state) {
+ if (tex_state != r_current_batch->tex_info.state) {
r_current_batch = _new_batch(r_batch_broken);
- r_current_batch->set_tex_state(tex_state);
- _prepare_batch_texture(r_current_batch, rect->texture);
+ r_current_batch->tex_info.state = tex_state;
+ _prepare_batch_texture_info(r_current_batch, rect->texture);
}
Color modulated = rect->modulate * base_color;
@@ -2399,7 +2405,7 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
Rect2 dst_rect;
if (rect->texture.is_valid()) {
- src_rect = (rect->flags & CANVAS_RECT_REGION) ? Rect2(rect->source.position * r_current_batch->tex_texpixel_size, rect->source.size * r_current_batch->tex_texpixel_size) : Rect2(0, 0, 1, 1);
+ src_rect = (rect->flags & CANVAS_RECT_REGION) ? Rect2(rect->source.position * r_current_batch->tex_info.texpixel_size, rect->source.size * r_current_batch->tex_info.texpixel_size) : Rect2(0, 0, 1, 1);
dst_rect = Rect2(rect->rect.position, rect->rect.size);
if (dst_rect.size.width < 0) {
@@ -2484,10 +2490,10 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
}
TextureState tex_state(np->texture, texture_filter, texture_repeat, false, use_linear_colors);
- if (tex_state != r_current_batch->tex_state) {
+ if (tex_state != r_current_batch->tex_info.state) {
r_current_batch = _new_batch(r_batch_broken);
- r_current_batch->set_tex_state(tex_state);
- _prepare_batch_texture(r_current_batch, np->texture);
+ r_current_batch->tex_info.state = tex_state;
+ _prepare_batch_texture_info(r_current_batch, np->texture);
}
InstanceData *instance_data = new_instance_data();
@@ -2499,7 +2505,7 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
src_rect = Rect2(0, 0, 1, 1);
} else {
if (np->source != Rect2()) {
- src_rect = Rect2(np->source.position.x * r_current_batch->tex_texpixel_size.width, np->source.position.y * r_current_batch->tex_texpixel_size.height, np->source.size.x * r_current_batch->tex_texpixel_size.width, np->source.size.y * r_current_batch->tex_texpixel_size.height);
+ src_rect = Rect2(np->source.position.x * r_current_batch->tex_info.texpixel_size.width, np->source.position.y * r_current_batch->tex_info.texpixel_size.height, np->source.size.x * r_current_batch->tex_info.texpixel_size.width, np->source.size.y * r_current_batch->tex_info.texpixel_size.height);
instance_data->color_texture_pixel_size[0] = 1.0 / np->source.size.width;
instance_data->color_texture_pixel_size[1] = 1.0 / np->source.size.height;
} else {
@@ -2553,10 +2559,10 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
r_current_batch->command = c;
TextureState tex_state(polygon->texture, texture_filter, texture_repeat, false, use_linear_colors);
- if (tex_state != r_current_batch->tex_state) {
+ if (tex_state != r_current_batch->tex_info.state) {
r_current_batch = _new_batch(r_batch_broken);
- r_current_batch->set_tex_state(tex_state);
- _prepare_batch_texture(r_current_batch, polygon->texture);
+ r_current_batch->tex_info.state = tex_state;
+ _prepare_batch_texture_info(r_current_batch, polygon->texture);
}
// pipeline variant
@@ -2596,10 +2602,10 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
r_current_batch->pipeline_variant = variant[primitive->point_count - 1];
TextureState tex_state(primitive->texture, texture_filter, texture_repeat, false, use_linear_colors);
- if (tex_state != r_current_batch->tex_state) {
+ if (tex_state != r_current_batch->tex_info.state) {
r_current_batch = _new_batch(r_batch_broken);
- r_current_batch->set_tex_state(tex_state);
- _prepare_batch_texture(r_current_batch, primitive->texture);
+ r_current_batch->tex_info.state = tex_state;
+ _prepare_batch_texture_info(r_current_batch, primitive->texture);
}
}
@@ -2657,8 +2663,8 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
if (c->type == Item::Command::TYPE_MESH) {
const Item::CommandMesh *m = static_cast<const Item::CommandMesh *>(c);
TextureState tex_state(m->texture, texture_filter, texture_repeat, false, use_linear_colors);
- r_current_batch->set_tex_state(tex_state);
- _prepare_batch_texture(r_current_batch, m->texture);
+ r_current_batch->tex_info.state = tex_state;
+ _prepare_batch_texture_info(r_current_batch, m->texture);
instance_data = new_instance_data();
r_current_batch->mesh_instance_count = 1;
@@ -2680,8 +2686,8 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
}
TextureState tex_state(mm->texture, texture_filter, texture_repeat, false, use_linear_colors);
- r_current_batch->set_tex_state(tex_state);
- _prepare_batch_texture(r_current_batch, mm->texture);
+ r_current_batch->tex_info.state = tex_state;
+ _prepare_batch_texture_info(r_current_batch, mm->texture);
instance_data = new_instance_data();
instance_data->flags |= 1; // multimesh, trails disabled
@@ -2698,8 +2704,8 @@ void RendererCanvasRenderRD::_record_item_commands(const Item *p_item, RenderTar
const Item::CommandParticles *pt = static_cast<const Item::CommandParticles *>(c);
TextureState tex_state(pt->texture, texture_filter, texture_repeat, false, use_linear_colors);
- r_current_batch->set_tex_state(tex_state);
- _prepare_batch_texture(r_current_batch, pt->texture);
+ r_current_batch->tex_info.state = tex_state;
+ _prepare_batch_texture_info(r_current_batch, pt->texture);
instance_data = new_instance_data();
@@ -2795,7 +2801,20 @@ void RendererCanvasRenderRD::_render_batch(RD::DrawListID p_draw_list, PipelineV
ERR_FAIL_NULL(p_batch->command);
- _bind_canvas_texture(p_draw_list, p_batch->tex_uniform_set);
+ {
+ RD::Uniform u_diffuse(RD::UNIFORM_TYPE_TEXTURE, 0, p_batch->tex_info.diffuse);
+ RD::Uniform u_normal(RD::UNIFORM_TYPE_TEXTURE, 1, p_batch->tex_info.normal);
+ RD::Uniform u_specular(RD::UNIFORM_TYPE_TEXTURE, 2, p_batch->tex_info.specular);
+ RD::Uniform u_sampler(RD::UNIFORM_TYPE_SAMPLER, 3, p_batch->tex_info.sampler);
+ RD::Uniform u_instance_data(RD::UNIFORM_TYPE_STORAGE_BUFFER, 4, state.canvas_instance_data_buffers[state.current_data_buffer_index].instance_buffers[p_batch->instance_buffer_index]);
+
+ RID uniform_set = uniform_set_cache->get_cache(shader.default_version_rd_shader, BATCH_UNIFORM_SET, u_diffuse, u_normal, u_specular, u_sampler, u_instance_data);
+
+ if (state.current_batch_uniform_set != uniform_set) {
+ state.current_batch_uniform_set = uniform_set;
+ RD::get_singleton()->draw_list_bind_uniform_set(p_draw_list, uniform_set, BATCH_UNIFORM_SET);
+ }
+ }
switch (p_batch->command_type) {
case Item::Command::TYPE_RECT:
@@ -2810,13 +2829,6 @@ void RendererCanvasRenderRD::_render_batch(RD::DrawListID p_draw_list, PipelineV
PushConstant push_constant;
push_constant.base_instance_index = p_batch->start;
RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(PushConstant));
-
- RD::Uniform u_instance_data(RD::UNIFORM_TYPE_STORAGE_BUFFER, 0, state.canvas_instance_data_buffers[state.current_data_buffer_index].instance_buffers[p_batch->instance_buffer_index]);
- RD::get_singleton()->draw_list_bind_uniform_set(
- p_draw_list,
- uniform_set_cache->get_cache(shader.default_version_rd_shader, INSTANCE_DATA_UNIFORM_SET, u_instance_data),
- INSTANCE_DATA_UNIFORM_SET);
-
RD::get_singleton()->draw_list_bind_index_array(p_draw_list, shader.quad_index_array);
RD::get_singleton()->draw_list_draw(p_draw_list, true, p_batch->instance_count);
@@ -2839,13 +2851,6 @@ void RendererCanvasRenderRD::_render_batch(RD::DrawListID p_draw_list, PipelineV
PushConstant push_constant;
push_constant.base_instance_index = p_batch->start;
RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(PushConstant));
-
- RD::Uniform u_instance_data(RD::UNIFORM_TYPE_STORAGE_BUFFER, 0, state.canvas_instance_data_buffers[state.current_data_buffer_index].instance_buffers[p_batch->instance_buffer_index]);
- RD::get_singleton()->draw_list_bind_uniform_set(
- p_draw_list,
- uniform_set_cache->get_cache(shader.default_version_rd_shader, INSTANCE_DATA_UNIFORM_SET, u_instance_data),
- INSTANCE_DATA_UNIFORM_SET);
-
RD::get_singleton()->draw_list_bind_vertex_array(p_draw_list, pb->vertex_array);
if (pb->indices.is_valid()) {
RD::get_singleton()->draw_list_bind_index_array(p_draw_list, pb->indices);
@@ -2868,13 +2873,6 @@ void RendererCanvasRenderRD::_render_batch(RD::DrawListID p_draw_list, PipelineV
PushConstant push_constant;
push_constant.base_instance_index = p_batch->start;
RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(PushConstant));
-
- RD::Uniform u_instance_data(RD::UNIFORM_TYPE_STORAGE_BUFFER, 0, state.canvas_instance_data_buffers[state.current_data_buffer_index].instance_buffers[p_batch->instance_buffer_index]);
- RD::get_singleton()->draw_list_bind_uniform_set(
- p_draw_list,
- uniform_set_cache->get_cache(shader.default_version_rd_shader, INSTANCE_DATA_UNIFORM_SET, u_instance_data),
- INSTANCE_DATA_UNIFORM_SET);
-
RD::get_singleton()->draw_list_bind_index_array(p_draw_list, primitive_arrays.index_array[MIN(3u, primitive->point_count) - 1]);
uint32_t instance_count = p_batch->instance_count;
RD::get_singleton()->draw_list_draw(p_draw_list, true, instance_count);
@@ -2934,12 +2932,6 @@ void RendererCanvasRenderRD::_render_batch(RD::DrawListID p_draw_list, PipelineV
break;
}
- RD::Uniform u_instance_data(RD::UNIFORM_TYPE_STORAGE_BUFFER, 0, state.canvas_instance_data_buffers[state.current_data_buffer_index].instance_buffers[p_batch->instance_buffer_index]);
- RD::get_singleton()->draw_list_bind_uniform_set(
- p_draw_list,
- uniform_set_cache->get_cache(shader.default_version_rd_shader, INSTANCE_DATA_UNIFORM_SET, u_instance_data),
- INSTANCE_DATA_UNIFORM_SET);
-
uint32_t surf_count = mesh_storage->mesh_get_surface_count(mesh);
static const PipelineVariant variant[RS::PRIMITIVE_MAX] = { PIPELINE_VARIANT_ATTRIBUTE_POINTS, PIPELINE_VARIANT_ATTRIBUTE_LINES, PIPELINE_VARIANT_ATTRIBUTE_LINES_STRIP, PIPELINE_VARIANT_ATTRIBUTE_TRIANGLES, PIPELINE_VARIANT_ATTRIBUTE_TRIANGLE_STRIP };
@@ -3046,60 +3038,46 @@ void RendererCanvasRenderRD::_allocate_instance_buffer() {
state.canvas_instance_data_buffers[state.current_data_buffer_index].instance_buffers.push_back(buf);
}
-void RendererCanvasRenderRD::_prepare_batch_texture(Batch *p_current_batch, RID p_texture) const {
+void RendererCanvasRenderRD::_prepare_batch_texture_info(Batch *p_current_batch, RID p_texture) const {
if (p_texture.is_null()) {
p_texture = default_canvas_texture;
}
- Color specular_shininess;
- bool use_normal;
- bool use_specular;
- Size2i size;
- bool success = RendererRD::TextureStorage::get_singleton()->canvas_texture_get_uniform_set(
- p_texture,
- p_current_batch->tex_state.texture_filter(),
- p_current_batch->tex_state.texture_repeat(),
- shader.default_version_rd_shader,
- CANVAS_TEXTURE_UNIFORM_SET,
- p_current_batch->tex_state.linear_colors(),
- p_current_batch->tex_uniform_set,
- size,
- specular_shininess,
- use_normal,
- use_specular,
- p_current_batch->tex_state.texture_is_data());
+ RendererRD::TextureStorage::CanvasTextureInfo info =
+ RendererRD::TextureStorage::get_singleton()->canvas_texture_get_info(
+ p_texture,
+ p_current_batch->tex_info.state.texture_filter(),
+ p_current_batch->tex_info.state.texture_repeat(),
+ p_current_batch->tex_info.state.linear_colors(),
+ p_current_batch->tex_info.state.texture_is_data());
+
// something odd happened
- if (!success) {
- _prepare_batch_texture(p_current_batch, default_canvas_texture);
+ if (info.is_null()) {
+ _prepare_batch_texture_info(p_current_batch, default_canvas_texture);
return;
}
- // cache values to be copied to instance data
- if (specular_shininess.a < 0.999) {
- p_current_batch->tex_flags |= FLAGS_DEFAULT_SPECULAR_MAP_USED;
- }
+ p_current_batch->tex_info.diffuse = info.diffuse;
+ p_current_batch->tex_info.normal = info.normal;
+ p_current_batch->tex_info.specular = info.specular;
+ p_current_batch->tex_info.sampler = info.sampler;
- if (use_normal) {
- p_current_batch->tex_flags |= FLAGS_DEFAULT_NORMAL_MAP_USED;
+ // cache values to be copied to instance data
+ if (info.specular_color.a < 0.999) {
+ p_current_batch->tex_info.flags |= FLAGS_DEFAULT_SPECULAR_MAP_USED;
}
- uint8_t a = uint8_t(CLAMP(specular_shininess.a * 255.0, 0.0, 255.0));
- uint8_t b = uint8_t(CLAMP(specular_shininess.b * 255.0, 0.0, 255.0));
- uint8_t g = uint8_t(CLAMP(specular_shininess.g * 255.0, 0.0, 255.0));
- uint8_t r = uint8_t(CLAMP(specular_shininess.r * 255.0, 0.0, 255.0));
- p_current_batch->tex_specular_shininess = uint32_t(a) << 24 | uint32_t(b) << 16 | uint32_t(g) << 8 | uint32_t(r);
-
- p_current_batch->tex_texpixel_size = Vector2(1.0 / float(size.width), 1.0 / float(size.height));
-}
-
-void RendererCanvasRenderRD::_bind_canvas_texture(RD::DrawListID p_draw_list, RID p_uniform_set) {
- if (state.current_tex_uniform_set == p_uniform_set) {
- return;
+ if (info.use_normal) {
+ p_current_batch->tex_info.flags |= FLAGS_DEFAULT_NORMAL_MAP_USED;
}
- state.current_tex_uniform_set = p_uniform_set;
+ uint8_t a = uint8_t(CLAMP(info.specular_color.a * 255.0, 0.0, 255.0));
+ uint8_t b = uint8_t(CLAMP(info.specular_color.b * 255.0, 0.0, 255.0));
+ uint8_t g = uint8_t(CLAMP(info.specular_color.g * 255.0, 0.0, 255.0));
+ uint8_t r = uint8_t(CLAMP(info.specular_color.r * 255.0, 0.0, 255.0));
+ p_current_batch->tex_info.specular_shininess = uint32_t(a) << 24 | uint32_t(b) << 16 | uint32_t(g) << 8 | uint32_t(r);
- RD::get_singleton()->draw_list_bind_uniform_set(p_draw_list, p_uniform_set, CANVAS_TEXTURE_UNIFORM_SET);
+ p_current_batch->tex_info.texpixel_size = Vector2(1.0 / float(info.size.width), 1.0 / float(info.size.height));
}
RendererCanvasRenderRD::~RendererCanvasRenderRD() {
diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h
index 8d90cd23ce..b0f4a4595a 100644
--- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h
+++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h
@@ -45,8 +45,7 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
BASE_UNIFORM_SET = 0,
MATERIAL_UNIFORM_SET = 1,
TRANSFORMS_UNIFORM_SET = 2,
- CANVAS_TEXTURE_UNIFORM_SET = 3,
- INSTANCE_DATA_UNIFORM_SET = 4,
+ BATCH_UNIFORM_SET = 3,
};
const int SAMPLERS_BINDING_FIRST_INDEX = 10;
@@ -423,24 +422,25 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
}
};
+ struct TextureInfo {
+ TextureState state;
+ uint32_t specular_shininess = 0;
+ uint32_t flags = 0;
+ Vector2 texpixel_size;
+
+ RID diffuse;
+ RID normal;
+ RID specular;
+ RID sampler;
+ };
+
struct Batch {
// Position in the UBO measured in bytes
uint32_t start = 0;
uint32_t instance_count = 0;
uint32_t instance_buffer_index = 0;
- TextureState tex_state;
- RID tex_uniform_set;
-
- // The following tex_ prefixed fields are used to cache the texture data for the current batch.
- // These values are applied to new InstanceData for the batch
-
- // The cached specular shininess derived from the current texture.
- uint32_t tex_specular_shininess = 0;
- // The cached texture flags, such as FLAGS_DEFAULT_SPECULAR_MAP_USED and FLAGS_DEFAULT_NORMAL_MAP_USED
- uint32_t tex_flags = 0;
- // The cached texture pixel size.
- Vector2 tex_texpixel_size;
+ TextureInfo tex_info;
Color modulate = Color(1.0, 1.0, 1.0, 1.0);
@@ -462,14 +462,6 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
uint32_t mesh_instance_count;
};
bool has_blend = false;
-
- void set_tex_state(TextureState &p_tex_state) {
- tex_state = p_tex_state;
- tex_uniform_set = RID();
- tex_texpixel_size = Size2();
- tex_specular_shininess = 0;
- tex_flags = 0;
- }
};
struct DataBuffer {
@@ -509,7 +501,7 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
uint32_t max_instances_per_buffer = 16384;
uint32_t max_instance_buffer_size = 16384 * sizeof(InstanceData);
- RID current_tex_uniform_set;
+ RID current_batch_uniform_set;
LightUniform *light_uniforms = nullptr;
@@ -532,6 +524,8 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
Item *items[MAX_RENDER_ITEMS];
+ TextureInfo default_texture_info;
+
bool using_directional_lights = false;
RID default_canvas_texture;
@@ -561,8 +555,7 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
void _render_batch_items(RenderTarget p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool &r_sdf_used, bool p_to_backbuffer = false, RenderingMethod::RenderInfo *r_render_info = nullptr);
void _record_item_commands(const Item *p_item, RenderTarget p_render_target, const Transform2D &p_base_transform, Item *&r_current_clip, Light *p_lights, uint32_t &r_index, bool &r_batch_broken, bool &r_sdf_used, Batch *&r_current_batch);
void _render_batch(RD::DrawListID p_draw_list, PipelineVariants *p_pipeline_variants, RenderingDevice::FramebufferFormatID p_framebuffer_format, Light *p_lights, Batch const *p_batch, RenderingMethod::RenderInfo *r_render_info = nullptr);
- void _prepare_batch_texture(Batch *p_current_batch, RID p_texture) const;
- void _bind_canvas_texture(RD::DrawListID p_draw_list, RID p_uniform_set);
+ void _prepare_batch_texture_info(Batch *p_current_batch, RID p_texture) const;
[[nodiscard]] Batch *_new_batch(bool &r_batch_broken);
void _add_to_batch(uint32_t &r_index, bool &r_batch_broken, Batch *&r_current_batch);
void _allocate_instance_buffer();
diff --git a/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl b/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl
index 7cf5b4576e..fc9b727581 100644
--- a/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl
+++ b/servers/rendering/renderer_rd/shaders/canvas_uniforms_inc.glsl
@@ -54,11 +54,6 @@ struct InstanceData {
uint lights[4];
};
-layout(set = 4, binding = 0, std430) restrict readonly buffer DrawData {
- InstanceData data[];
-}
-instances;
-
layout(push_constant, std430) uniform Params {
uint base_instance_index; // base index to instance data
uint pad1;
@@ -163,3 +158,8 @@ layout(set = 3, binding = 0) uniform texture2D color_texture;
layout(set = 3, binding = 1) uniform texture2D normal_texture;
layout(set = 3, binding = 2) uniform texture2D specular_texture;
layout(set = 3, binding = 3) uniform sampler texture_sampler;
+
+layout(set = 3, binding = 4, std430) restrict readonly buffer DrawData {
+ InstanceData data[];
+}
+instances;
diff --git a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl
index 400451ec36..3cbe1427b6 100644
--- a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl
+++ b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl
@@ -156,8 +156,30 @@ vec2 multiview_uv(vec2 uv) {
ivec2 multiview_uv(ivec2 uv) {
return uv;
}
+
#endif //USE_MULTIVIEW
+#if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) && defined(USE_VERTEX_LIGHTING)
+layout(location = 12) highp out vec4 diffuse_light_interp;
+layout(location = 13) highp out vec4 specular_light_interp;
+
+#include "../scene_forward_vertex_lights_inc.glsl"
+
+void cluster_get_item_range(uint p_offset, out uint item_min, out uint item_max, out uint item_from, out uint item_to) {
+ uint item_min_max = cluster_buffer.data[p_offset];
+ item_min = item_min_max & 0xFFFFu;
+ item_max = item_min_max >> 16;
+
+ item_from = item_min >> 5;
+ item_to = (item_max == 0) ? 0 : ((item_max - 1) >> 5) + 1; //side effect of how it is stored, as item_max 0 means no elements
+}
+
+uint cluster_get_range_clip_mask(uint i, uint z_min, uint z_max) {
+ int local_min = clamp(int(z_min) - int(i) * 32, 0, 31);
+ int mask_width = min(int(z_max) - int(z_min), 32 - local_min);
+ return bitfieldInsert(uint(0), uint(0xFFFFFFFF), local_min, mask_width);
+}
+#endif // !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) && defined(USE_VERTEX_LIGHTING)
invariant gl_Position;
#GLOBALS
@@ -488,6 +510,145 @@ void vertex_shader(vec3 vertex_input,
screen_pos = gl_Position;
#endif
+#if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) && defined(USE_VERTEX_LIGHTING)
+ diffuse_light_interp = vec4(0.0);
+ specular_light_interp = vec4(0.0);
+
+#ifdef USE_MULTIVIEW
+ vec3 view = -normalize(vertex_interp - eye_offset);
+ vec2 clip_pos = clamp((combined_projected.xy / combined_projected.w) * 0.5 + 0.5, 0.0, 1.0);
+#else
+ vec3 view = -normalize(vertex_interp);
+ vec2 clip_pos = clamp((gl_Position.xy / gl_Position.w) * 0.5 + 0.5, 0.0, 1.0);
+#endif
+
+ uvec2 cluster_pos = uvec2(clip_pos / scene_data.screen_pixel_size) >> implementation_data.cluster_shift;
+ uint cluster_offset = (implementation_data.cluster_width * cluster_pos.y + cluster_pos.x) * (implementation_data.max_cluster_element_count_div_32 + 32);
+ uint cluster_z = uint(clamp((-vertex_interp.z / scene_data.z_far) * 32.0, 0.0, 31.0));
+
+ { //omni lights
+
+ uint cluster_omni_offset = cluster_offset;
+
+ uint item_min;
+ uint item_max;
+ uint item_from;
+ uint item_to;
+
+ cluster_get_item_range(cluster_omni_offset + implementation_data.max_cluster_element_count_div_32 + cluster_z, item_min, item_max, item_from, item_to);
+
+ for (uint i = item_from; i < item_to; i++) {
+ uint mask = cluster_buffer.data[cluster_omni_offset + i];
+ mask &= cluster_get_range_clip_mask(i, item_min, item_max);
+ uint merged_mask = mask;
+
+ while (merged_mask != 0) {
+ uint bit = findMSB(merged_mask);
+ merged_mask &= ~(1u << bit);
+ uint light_index = 32 * i + bit;
+
+ if (!bool(omni_lights.data[light_index].mask & instances.data[instance_index].layer_mask)) {
+ continue; //not masked
+ }
+
+ if (omni_lights.data[light_index].bake_mode == LIGHT_BAKE_STATIC && bool(instances.data[instance_index].flags & INSTANCE_FLAGS_USE_LIGHTMAP)) {
+ continue; // Statically baked light and object uses lightmap, skip
+ }
+
+ light_process_omni_vertex(light_index, vertex, view, normal, roughness,
+ diffuse_light_interp.rgb, specular_light_interp.rgb);
+ }
+ }
+ }
+
+ { //spot lights
+ uint cluster_spot_offset = cluster_offset + implementation_data.cluster_type_size;
+
+ uint item_min;
+ uint item_max;
+ uint item_from;
+ uint item_to;
+
+ cluster_get_item_range(cluster_spot_offset + implementation_data.max_cluster_element_count_div_32 + cluster_z, item_min, item_max, item_from, item_to);
+
+ for (uint i = item_from; i < item_to; i++) {
+ uint mask = cluster_buffer.data[cluster_spot_offset + i];
+ mask &= cluster_get_range_clip_mask(i, item_min, item_max);
+ uint merged_mask = mask;
+
+ while (merged_mask != 0) {
+ uint bit = findMSB(merged_mask);
+ merged_mask &= ~(1u << bit);
+
+ uint light_index = 32 * i + bit;
+
+ if (!bool(spot_lights.data[light_index].mask & instances.data[instance_index].layer_mask)) {
+ continue; //not masked
+ }
+
+ if (spot_lights.data[light_index].bake_mode == LIGHT_BAKE_STATIC && bool(instances.data[instance_index].flags & INSTANCE_FLAGS_USE_LIGHTMAP)) {
+ continue; // Statically baked light and object uses lightmap, skip
+ }
+
+ light_process_spot_vertex(light_index, vertex, view, normal, roughness,
+ diffuse_light_interp.rgb, specular_light_interp.rgb);
+ }
+ }
+ }
+
+ { // Directional light.
+
+ // We process the first directional light separately as it may have shadows.
+ vec3 directional_diffuse = vec3(0.0);
+ vec3 directional_specular = vec3(0.0);
+
+ for (uint i = 0; i < scene_data.directional_light_count; i++) {
+ if (!bool(directional_lights.data[i].mask & instances.data[draw_call.instance_index].layer_mask)) {
+ continue; // Not masked, skip.
+ }
+
+ if (directional_lights.data[i].bake_mode == LIGHT_BAKE_STATIC && bool(instances.data[draw_call.instance_index].flags & INSTANCE_FLAGS_USE_LIGHTMAP)) {
+ continue; // Statically baked light and object uses lightmap, skip.
+ }
+ if (i == 0) {
+ light_compute_vertex(normal, directional_lights.data[0].direction, view,
+ directional_lights.data[0].color * directional_lights.data[0].energy,
+ true, roughness,
+ directional_diffuse,
+ directional_specular);
+ } else {
+ light_compute_vertex(normal, directional_lights.data[i].direction, view,
+ directional_lights.data[i].color * directional_lights.data[i].energy,
+ true, roughness,
+ diffuse_light_interp.rgb,
+ specular_light_interp.rgb);
+ }
+ }
+
+ // Calculate the contribution from the shadowed light so we can scale the shadows accordingly.
+ float diff_avg = dot(diffuse_light_interp.rgb, vec3(0.33333));
+ float diff_dir_avg = dot(directional_diffuse, vec3(0.33333));
+ if (diff_avg > 0.0) {
+ diffuse_light_interp.a = diff_dir_avg / (diff_avg + diff_dir_avg);
+ } else {
+ diffuse_light_interp.a = 1.0;
+ }
+
+ diffuse_light_interp.rgb += directional_diffuse;
+
+ float spec_avg = dot(specular_light_interp.rgb, vec3(0.33333));
+ float spec_dir_avg = dot(directional_specular, vec3(0.33333));
+ if (spec_avg > 0.0) {
+ specular_light_interp.a = spec_dir_avg / (spec_avg + spec_dir_avg);
+ } else {
+ specular_light_interp.a = 1.0;
+ }
+
+ specular_light_interp.rgb += directional_specular;
+ }
+
+#endif //!defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) && defined(USE_VERTEX_LIGHTING)
+
#ifdef MODE_RENDER_DEPTH
if (scene_data.pancake_shadows) {
if (gl_Position.z >= 0.9999) {
@@ -791,7 +952,10 @@ ivec2 multiview_uv(ivec2 uv) {
return uv;
}
#endif //USE_MULTIVIEW
-
+#if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) && defined(USE_VERTEX_LIGHTING)
+layout(location = 12) highp in vec4 diffuse_light_interp;
+layout(location = 13) highp in vec4 specular_light_interp;
+#endif
//defines to keep compatibility with vertex
#ifdef USE_MULTIVIEW
@@ -1375,7 +1539,6 @@ void fragment_shader(in SceneData scene_data) {
vec3 specular_light = vec3(0.0, 0.0, 0.0);
vec3 diffuse_light = vec3(0.0, 0.0, 0.0);
vec3 ambient_light = vec3(0.0, 0.0, 0.0);
-
#ifndef MODE_UNSHADED
// Used in regular draw pass and when drawing SDFs for SDFGI and materials for VoxelGI.
emission *= scene_data.emissive_exposure_normalization;
@@ -1836,6 +1999,11 @@ void fragment_shader(in SceneData scene_data) {
// LIGHTING
#if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED)
+#ifdef USE_VERTEX_LIGHTING
+ diffuse_light += diffuse_light_interp.rgb;
+ specular_light += specular_light_interp.rgb * f0;
+#endif
+
{ // Directional light.
// Do shadow and lighting in two passes to reduce register pressure.
@@ -1843,10 +2011,15 @@ void fragment_shader(in SceneData scene_data) {
uint shadow0 = 0;
uint shadow1 = 0;
+#ifdef USE_VERTEX_LIGHTING
+ // Only process the first light's shadow for vertex lighting.
+ for (uint i = 0; i < 1; i++) {
+#else
for (uint i = 0; i < 8; i++) {
if (i >= scene_data.directional_light_count) {
break;
}
+#endif
if (!bool(directional_lights.data[i].mask & instances.data[instance_index].layer_mask)) {
continue; //not masked
@@ -2044,6 +2217,11 @@ void fragment_shader(in SceneData scene_data) {
shadow = mix(shadow, 1.0, smoothstep(directional_lights.data[i].fade_from, directional_lights.data[i].fade_to, vertex.z)); //done with negative values for performance
+#ifdef USE_VERTEX_LIGHTING
+ diffuse_light *= mix(1.0, shadow, diffuse_light_interp.a);
+ specular_light *= mix(1.0, shadow, specular_light_interp.a);
+#endif
+
#undef BIAS_FUNC
} // shadows
@@ -2055,6 +2233,8 @@ void fragment_shader(in SceneData scene_data) {
}
#endif // SHADOWS_DISABLED
+#ifndef USE_VERTEX_LIGHTING
+
for (uint i = 0; i < 8; i++) {
if (i >= scene_data.directional_light_count) {
break;
@@ -2175,8 +2355,10 @@ void fragment_shader(in SceneData scene_data) {
diffuse_light,
specular_light);
}
+#endif // USE_VERTEX_LIGHTING
}
+#ifndef USE_VERTEX_LIGHTING
{ //omni lights
uint cluster_omni_offset = cluster_offset;
@@ -2320,6 +2502,8 @@ void fragment_shader(in SceneData scene_data) {
}
}
}
+#endif // !USE_VERTEX_LIGHTING
+#endif //!defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED)
#ifdef USE_SHADOW_TO_OPACITY
#ifndef MODE_RENDER_DEPTH
@@ -2334,8 +2518,6 @@ void fragment_shader(in SceneData scene_data) {
#endif // !MODE_RENDER_DEPTH
#endif // USE_SHADOW_TO_OPACITY
-#endif //!defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED)
-
#ifdef MODE_RENDER_DEPTH
#ifdef MODE_RENDER_SDF
diff --git a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl
index 17c7b756c3..e7ce44bce2 100644
--- a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl
+++ b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl
@@ -105,7 +105,16 @@ layout(location = 4) mediump out vec2 uv2_interp;
layout(location = 5) mediump out vec3 tangent_interp;
layout(location = 6) mediump out vec3 binormal_interp;
#endif
+#if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) && defined(USE_VERTEX_LIGHTING)
+layout(location = 7) highp out vec4 diffuse_light_interp;
+layout(location = 8) highp out vec4 specular_light_interp;
+layout(constant_id = 9) const bool sc_disable_omni_lights = false;
+layout(constant_id = 10) const bool sc_disable_spot_lights = false;
+layout(constant_id = 12) const bool sc_disable_directional_lights = false;
+
+#include "../scene_forward_vertex_lights_inc.glsl"
+#endif // !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) && defined(USE_VERTEX_LIGHTING)
#ifdef MATERIAL_UNIFORMS_USED
/* clang-format off */
layout(set = MATERIAL_UNIFORM_SET, binding = 0, std140) uniform MaterialUniforms {
@@ -185,6 +194,7 @@ void main() {
mat4 model_matrix = instances.data[draw_call.instance_index].transform;
mat4 inv_view_matrix = scene_data.inv_view_matrix;
+
#ifdef USE_DOUBLE_PRECISION
vec3 model_precision = vec3(model_matrix[0][3], model_matrix[1][3], model_matrix[2][3]);
model_matrix[0][3] = 0.0;
@@ -448,6 +458,107 @@ void main() {
binormal_interp = binormal;
#endif
+// VERTEX LIGHTING
+#if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) && defined(USE_VERTEX_LIGHTING)
+#ifdef USE_MULTIVIEW
+ vec3 view = -normalize(vertex_interp - eye_offset);
+#else
+ vec3 view = -normalize(vertex_interp);
+#endif
+
+ diffuse_light_interp = vec4(0.0);
+ specular_light_interp = vec4(0.0);
+
+ if (!sc_disable_omni_lights) {
+ uint light_indices = instances.data[draw_call.instance_index].omni_lights.x;
+ for (uint i = 0; i < 8; i++) {
+ uint light_index = light_indices & 0xFF;
+ if (i == 3) {
+ light_indices = instances.data[draw_call.instance_index].omni_lights.y;
+ } else {
+ light_indices = light_indices >> 8;
+ }
+
+ if (light_index == 0xFF) {
+ break;
+ }
+
+ light_process_omni_vertex(light_index, vertex, view, normal, roughness,
+ diffuse_light_interp.rgb, specular_light_interp.rgb);
+ }
+ }
+
+ if (!sc_disable_spot_lights) {
+ uint light_indices = instances.data[draw_call.instance_index].spot_lights.x;
+ for (uint i = 0; i < 8; i++) {
+ uint light_index = light_indices & 0xFF;
+ if (i == 3) {
+ light_indices = instances.data[draw_call.instance_index].spot_lights.y;
+ } else {
+ light_indices = light_indices >> 8;
+ }
+
+ if (light_index == 0xFF) {
+ break;
+ }
+
+ light_process_spot_vertex(light_index, vertex, view, normal, roughness,
+ diffuse_light_interp.rgb, specular_light_interp.rgb);
+ }
+ }
+
+ if (!sc_disable_directional_lights) {
+ // We process the first directional light separately as it may have shadows.
+ vec3 directional_diffuse = vec3(0.0);
+ vec3 directional_specular = vec3(0.0);
+
+ for (uint i = 0; i < scene_data.directional_light_count; i++) {
+ if (!bool(directional_lights.data[i].mask & instances.data[draw_call.instance_index].layer_mask)) {
+ continue; // Not masked, skip.
+ }
+
+ if (directional_lights.data[i].bake_mode == LIGHT_BAKE_STATIC && bool(instances.data[draw_call.instance_index].flags & INSTANCE_FLAGS_USE_LIGHTMAP)) {
+ continue; // Statically baked light and object uses lightmap, skip.
+ }
+ if (i == 0) {
+ light_compute_vertex(normal, directional_lights.data[0].direction, view,
+ directional_lights.data[0].color * directional_lights.data[0].energy,
+ true, roughness,
+ directional_diffuse,
+ directional_specular);
+ } else {
+ light_compute_vertex(normal, directional_lights.data[i].direction, view,
+ directional_lights.data[i].color * directional_lights.data[i].energy,
+ true, roughness,
+ diffuse_light_interp.rgb,
+ specular_light_interp.rgb);
+ }
+ }
+
+ // Calculate the contribution from the shadowed light so we can scale the shadows accordingly.
+ float diff_avg = dot(diffuse_light_interp.rgb, vec3(0.33333));
+ float diff_dir_avg = dot(directional_diffuse, vec3(0.33333));
+ if (diff_avg > 0.0) {
+ diffuse_light_interp.a = diff_dir_avg / (diff_avg + diff_dir_avg);
+ } else {
+ diffuse_light_interp.a = 1.0;
+ }
+
+ diffuse_light_interp.rgb += directional_diffuse;
+
+ float spec_avg = dot(specular_light_interp.rgb, vec3(0.33333));
+ float spec_dir_avg = dot(directional_specular, vec3(0.33333));
+ if (spec_avg > 0.0) {
+ specular_light_interp.a = spec_dir_avg / (spec_avg + spec_dir_avg);
+ } else {
+ specular_light_interp.a = 1.0;
+ }
+
+ specular_light_interp.rgb += directional_specular;
+ }
+
+#endif //!defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) && defined(USE_VERTEX_LIGHTING)
+
#ifdef MODE_RENDER_DEPTH
#ifdef MODE_DUAL_PARABOLOID
@@ -564,6 +675,11 @@ layout(location = 5) mediump in vec3 tangent_interp;
layout(location = 6) mediump in vec3 binormal_interp;
#endif
+#if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) && defined(USE_VERTEX_LIGHTING)
+layout(location = 7) highp in vec4 diffuse_light_interp;
+layout(location = 8) highp in vec4 specular_light_interp;
+#endif
+
#ifdef MODE_DUAL_PARABOLOID
layout(location = 9) highp in float dp_clip;
@@ -709,7 +825,7 @@ layout(location = 0) out mediump vec4 frag_color;
#include "../scene_forward_aa_inc.glsl"
-#if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED)
+#if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) // && !defined(USE_VERTEX_LIGHTING)
// Default to SPECULAR_SCHLICK_GGX.
#if !defined(SPECULAR_DISABLED) && !defined(SPECULAR_SCHLICK_GGX) && !defined(SPECULAR_TOON)
@@ -718,7 +834,7 @@ layout(location = 0) out mediump vec4 frag_color;
#include "../scene_forward_lights_inc.glsl"
-#endif //!defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED)
+#endif //!defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) && !defined(USE_VERTEX_LIGHTING)
#ifndef MODE_RENDER_DEPTH
@@ -1401,6 +1517,10 @@ void main() {
// LIGHTING
#if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED)
+#ifdef USE_VERTEX_LIGHTING
+ diffuse_light += diffuse_light_interp.rgb;
+ specular_light += specular_light_interp.rgb * f0;
+#endif
if (!sc_disable_directional_lights) { //directional light
#ifndef SHADOWS_DISABLED
@@ -1408,10 +1528,12 @@ void main() {
uint shadow0 = 0;
uint shadow1 = 0;
- for (uint i = 0; i < 8; i++) {
- if (i >= scene_data.directional_light_count) {
- break;
- }
+#ifdef USE_VERTEX_LIGHTING
+ // Only process the first light's shadow for vertex lighting.
+ for (uint i = 0; i < 1; i++) {
+#else
+ for (uint i = 0; i < scene_data.directional_light_count; i++) {
+#endif
if (!bool(directional_lights.data[i].mask & instances.data[draw_call.instance_index].layer_mask)) {
continue; //not masked
@@ -1419,164 +1541,6 @@ void main() {
float shadow = 1.0;
- // Directional light shadow code is basically the same as forward clustered at this point in time minus `LIGHT_TRANSMITTANCE_USED` support.
- // Not sure if there is a reason to change this seeing directional lights are part of our global data
- // Should think about whether we may want to move this code into an include file or function??
-
-#ifdef USE_SOFT_SHADOWS
- //version with soft shadows, more expensive
- if (directional_lights.data[i].shadow_opacity > 0.001) {
- float depth_z = -vertex.z;
-
- vec4 pssm_coord;
- vec3 light_dir = directional_lights.data[i].direction;
-
-#define BIAS_FUNC(m_var, m_idx) \
- m_var.xyz += light_dir * directional_lights.data[i].shadow_bias[m_idx]; \
- vec3 normal_bias = normalize(normal_interp) * (1.0 - max(0.0, dot(light_dir, -normalize(normal_interp)))) * directional_lights.data[i].shadow_normal_bias[m_idx]; \
- normal_bias -= light_dir * dot(light_dir, normal_bias); \
- m_var.xyz += normal_bias;
-
- if (depth_z < directional_lights.data[i].shadow_split_offsets.x) {
- vec4 v = vec4(vertex, 1.0);
-
- BIAS_FUNC(v, 0)
-
- pssm_coord = (directional_lights.data[i].shadow_matrix1 * v);
- pssm_coord /= pssm_coord.w;
-
- if (directional_lights.data[i].softshadow_angle > 0) {
- float range_pos = dot(directional_lights.data[i].direction, v.xyz);
- float range_begin = directional_lights.data[i].shadow_range_begin.x;
- float test_radius = (range_pos - range_begin) * directional_lights.data[i].softshadow_angle;
- vec2 tex_scale = directional_lights.data[i].uv_scale1 * test_radius;
- shadow = sample_directional_soft_shadow(directional_shadow_atlas, pssm_coord.xyz, tex_scale * directional_lights.data[i].soft_shadow_scale, scene_data.taa_frame_count);
- } else {
- shadow = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale, pssm_coord, scene_data.taa_frame_count);
- }
- } else if (depth_z < directional_lights.data[i].shadow_split_offsets.y) {
- vec4 v = vec4(vertex, 1.0);
-
- BIAS_FUNC(v, 1)
-
- pssm_coord = (directional_lights.data[i].shadow_matrix2 * v);
- pssm_coord /= pssm_coord.w;
-
- if (directional_lights.data[i].softshadow_angle > 0) {
- float range_pos = dot(directional_lights.data[i].direction, v.xyz);
- float range_begin = directional_lights.data[i].shadow_range_begin.y;
- float test_radius = (range_pos - range_begin) * directional_lights.data[i].softshadow_angle;
- vec2 tex_scale = directional_lights.data[i].uv_scale2 * test_radius;
- shadow = sample_directional_soft_shadow(directional_shadow_atlas, pssm_coord.xyz, tex_scale * directional_lights.data[i].soft_shadow_scale, scene_data.taa_frame_count);
- } else {
- shadow = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale, pssm_coord, scene_data.taa_frame_count);
- }
- } else if (depth_z < directional_lights.data[i].shadow_split_offsets.z) {
- vec4 v = vec4(vertex, 1.0);
-
- BIAS_FUNC(v, 2)
-
- pssm_coord = (directional_lights.data[i].shadow_matrix3 * v);
- pssm_coord /= pssm_coord.w;
-
- if (directional_lights.data[i].softshadow_angle > 0) {
- float range_pos = dot(directional_lights.data[i].direction, v.xyz);
- float range_begin = directional_lights.data[i].shadow_range_begin.z;
- float test_radius = (range_pos - range_begin) * directional_lights.data[i].softshadow_angle;
- vec2 tex_scale = directional_lights.data[i].uv_scale3 * test_radius;
- shadow = sample_directional_soft_shadow(directional_shadow_atlas, pssm_coord.xyz, tex_scale * directional_lights.data[i].soft_shadow_scale, scene_data.taa_frame_count);
- } else {
- shadow = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale, pssm_coord, scene_data.taa_frame_count);
- }
- } else {
- vec4 v = vec4(vertex, 1.0);
-
- BIAS_FUNC(v, 3)
-
- pssm_coord = (directional_lights.data[i].shadow_matrix4 * v);
- pssm_coord /= pssm_coord.w;
-
- if (directional_lights.data[i].softshadow_angle > 0) {
- float range_pos = dot(directional_lights.data[i].direction, v.xyz);
- float range_begin = directional_lights.data[i].shadow_range_begin.w;
- float test_radius = (range_pos - range_begin) * directional_lights.data[i].softshadow_angle;
- vec2 tex_scale = directional_lights.data[i].uv_scale4 * test_radius;
- shadow = sample_directional_soft_shadow(directional_shadow_atlas, pssm_coord.xyz, tex_scale * directional_lights.data[i].soft_shadow_scale, scene_data.taa_frame_count);
- } else {
- shadow = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale, pssm_coord, scene_data.taa_frame_count);
- }
- }
-
- if (directional_lights.data[i].blend_splits) {
- float pssm_blend;
- float shadow2;
-
- if (depth_z < directional_lights.data[i].shadow_split_offsets.x) {
- vec4 v = vec4(vertex, 1.0);
- BIAS_FUNC(v, 1)
- pssm_coord = (directional_lights.data[i].shadow_matrix2 * v);
- pssm_coord /= pssm_coord.w;
-
- if (directional_lights.data[i].softshadow_angle > 0) {
- float range_pos = dot(directional_lights.data[i].direction, v.xyz);
- float range_begin = directional_lights.data[i].shadow_range_begin.y;
- float test_radius = (range_pos - range_begin) * directional_lights.data[i].softshadow_angle;
- vec2 tex_scale = directional_lights.data[i].uv_scale2 * test_radius;
- shadow2 = sample_directional_soft_shadow(directional_shadow_atlas, pssm_coord.xyz, tex_scale * directional_lights.data[i].soft_shadow_scale, scene_data.taa_frame_count);
- } else {
- shadow2 = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale, pssm_coord, scene_data.taa_frame_count);
- }
-
- pssm_blend = smoothstep(0.0, directional_lights.data[i].shadow_split_offsets.x, depth_z);
- } else if (depth_z < directional_lights.data[i].shadow_split_offsets.y) {
- vec4 v = vec4(vertex, 1.0);
- BIAS_FUNC(v, 2)
- pssm_coord = (directional_lights.data[i].shadow_matrix3 * v);
- pssm_coord /= pssm_coord.w;
-
- if (directional_lights.data[i].softshadow_angle > 0) {
- float range_pos = dot(directional_lights.data[i].direction, v.xyz);
- float range_begin = directional_lights.data[i].shadow_range_begin.z;
- float test_radius = (range_pos - range_begin) * directional_lights.data[i].softshadow_angle;
- vec2 tex_scale = directional_lights.data[i].uv_scale3 * test_radius;
- shadow2 = sample_directional_soft_shadow(directional_shadow_atlas, pssm_coord.xyz, tex_scale * directional_lights.data[i].soft_shadow_scale, scene_data.taa_frame_count);
- } else {
- shadow2 = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale, pssm_coord, scene_data.taa_frame_count);
- }
-
- pssm_blend = smoothstep(directional_lights.data[i].shadow_split_offsets.x, directional_lights.data[i].shadow_split_offsets.y, depth_z);
- } else if (depth_z < directional_lights.data[i].shadow_split_offsets.z) {
- vec4 v = vec4(vertex, 1.0);
- BIAS_FUNC(v, 3)
- pssm_coord = (directional_lights.data[i].shadow_matrix4 * v);
- pssm_coord /= pssm_coord.w;
- if (directional_lights.data[i].softshadow_angle > 0) {
- float range_pos = dot(directional_lights.data[i].direction, v.xyz);
- float range_begin = directional_lights.data[i].shadow_range_begin.w;
- float test_radius = (range_pos - range_begin) * directional_lights.data[i].softshadow_angle;
- vec2 tex_scale = directional_lights.data[i].uv_scale4 * test_radius;
- shadow2 = sample_directional_soft_shadow(directional_shadow_atlas, pssm_coord.xyz, tex_scale * directional_lights.data[i].soft_shadow_scale, scene_data.taa_frame_count);
- } else {
- shadow2 = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale, pssm_coord, scene_data.taa_frame_count);
- }
-
- pssm_blend = smoothstep(directional_lights.data[i].shadow_split_offsets.y, directional_lights.data[i].shadow_split_offsets.z, depth_z);
- } else {
- pssm_blend = 0.0; //if no blend, same coord will be used (divide by z will result in same value, and already cached)
- }
-
- pssm_blend = sqrt(pssm_blend);
-
- shadow = mix(shadow, shadow2, pssm_blend);
- }
-
- shadow = mix(shadow, 1.0, smoothstep(directional_lights.data[i].fade_from, directional_lights.data[i].fade_to, vertex.z)); //done with negative values for performance
-
-#undef BIAS_FUNC
- }
-#else
- // Soft shadow disabled version
-
if (directional_lights.data[i].shadow_opacity > 0.001) {
float depth_z = -vertex.z;
@@ -1667,6 +1631,10 @@ void main() {
shadow = mix(shadow, 1.0, smoothstep(directional_lights.data[i].fade_from, directional_lights.data[i].fade_to, vertex.z)); //done with negative values for performance
+#ifdef USE_VERTEX_LIGHTING
+ diffuse_light *= mix(1.0, shadow, diffuse_light_interp.a);
+ specular_light *= mix(1.0, shadow, specular_light_interp.a);
+#endif
#undef BIAS_FUNC
}
#endif
@@ -1678,13 +1646,8 @@ void main() {
}
}
-#endif // SHADOWS_DISABLED
-
- for (uint i = 0; i < 8; i++) {
- if (i >= scene_data.directional_light_count) {
- break;
- }
-
+#ifndef USE_VERTEX_LIGHTING
+ for (uint i = 0; i < scene_data.directional_light_count; i++) {
if (!bool(directional_lights.data[i].mask & instances.data[draw_call.instance_index].layer_mask)) {
continue; //not masked
}
@@ -1703,8 +1666,8 @@ void main() {
#endif
blur_shadow(shadow);
-#ifdef DEBUG_DRAW_PSSM_SPLITS
vec3 tint = vec3(1.0);
+#ifdef DEBUG_DRAW_PSSM_SPLITS
if (-vertex.z < directional_lights.data[i].shadow_split_offsets.x) {
tint = vec3(1.0, 0.0, 0.0);
} else if (-vertex.z < directional_lights.data[i].shadow_split_offsets.y) {
@@ -1718,12 +1681,10 @@ void main() {
shadow = 1.0;
#endif
- light_compute(normal, directional_lights.data[i].direction, normalize(view), 0.0,
-#ifndef DEBUG_DRAW_PSSM_SPLITS
- directional_lights.data[i].color * directional_lights.data[i].energy,
-#else
+ float size_A = sc_use_light_soft_shadows ? directional_lights.data[i].size : 0.0;
+
+ light_compute(normal, directional_lights.data[i].direction, view, size_A,
directional_lights.data[i].color * directional_lights.data[i].energy * tint,
-#endif
true, shadow, f0, orms, 1.0, albedo, alpha,
#ifdef LIGHT_BACKLIGHT_USED
backlight,
@@ -1745,14 +1706,13 @@ void main() {
#ifdef LIGHT_ANISOTROPY_USED
binormal, tangent, anisotropy,
#endif
-#ifdef USE_SOFT_SHADOW
- directional_lights.data[i].size,
-#endif
diffuse_light,
specular_light);
}
+#endif // USE_VERTEX_LIGHTING
} //directional light
+#ifndef USE_VERTEX_LIGHTING
if (!sc_disable_omni_lights) { //omni lights
uint light_indices = instances.data[draw_call.instance_index].omni_lights.x;
for (uint i = 0; i < 8; i++) {
@@ -1771,6 +1731,7 @@ void main() {
shadow = blur_shadow(shadow);
+ // Fragment lighting
light_process_omni(light_index, vertex, view, normal, vertex_ddx, vertex_ddy, f0, orms, shadow, albedo, alpha,
#ifdef LIGHT_BACKLIGHT_USED
backlight,
@@ -1841,6 +1802,9 @@ void main() {
diffuse_light, specular_light);
}
} //spot lights
+#endif // !VERTEX_LIGHTING
+
+#endif //!defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED)
#ifdef USE_SHADOW_TO_OPACITY
#ifndef MODE_RENDER_DEPTH
@@ -1855,8 +1819,6 @@ void main() {
#endif // !MODE_RENDER_DEPTH
#endif // USE_SHADOW_TO_OPACITY
-#endif //!defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED)
-
#ifdef MODE_RENDER_DEPTH
#ifdef MODE_RENDER_MATERIAL
diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_vertex_lights_inc.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_vertex_lights_inc.glsl
new file mode 100644
index 0000000000..e962c8f7f3
--- /dev/null
+++ b/servers/rendering/renderer_rd/shaders/scene_forward_vertex_lights_inc.glsl
@@ -0,0 +1,82 @@
+// Simplified versions of light functions intended for the vertex shader.
+
+// Eyeballed approximation of `exp2(15.0 * (1.0 - roughness) + 1.0) * 0.25`.
+// Uses slightly more FMA instructions (2x rate) to avoid special instructions (0.25x rate).
+// Range is reduced to [0.64,4977] from [068,2,221,528] which makes mediump feasible for the rest of the shader.
+mediump float roughness_to_shininess(mediump float roughness) {
+ mediump float r = 1.2 - roughness;
+ mediump float r2 = r * r;
+ return r * r2 * r2 * 2000.0;
+}
+
+void light_compute_vertex(vec3 N, vec3 L, vec3 V, vec3 light_color, bool is_directional, float roughness,
+ inout vec3 diffuse_light, inout vec3 specular_light) {
+ float NdotL = min(dot(N, L), 1.0);
+ float cNdotL = max(NdotL, 0.0); // clamped NdotL
+
+#if defined(DIFFUSE_LAMBERT_WRAP)
+ // Energy conserving lambert wrap shader.
+ // https://web.archive.org/web/20210228210901/http://blog.stevemcauley.com/2011/12/03/energy-conserving-wrapped-diffuse/
+ float diffuse_brdf_NL = max(0.0, (cNdotL + roughness) / ((1.0 + roughness) * (1.0 + roughness))) * (1.0 / M_PI);
+#else
+ // lambert
+ float diffuse_brdf_NL = cNdotL * (1.0 / M_PI);
+#endif
+
+ diffuse_light += light_color * diffuse_brdf_NL;
+
+#if !defined(SPECULAR_DISABLED)
+ float specular_brdf_NL = 0.0;
+ // Normalized blinn always unless disabled.
+ vec3 H = normalize(V + L);
+ float cNdotH = clamp(dot(N, H), 0.0, 1.0);
+ float shininess = roughness_to_shininess(roughness);
+ float blinn = pow(cNdotH, shininess);
+ blinn *= (shininess + 2.0) * (1.0 / (8.0 * M_PI)) * cNdotL;
+ specular_brdf_NL = blinn;
+ specular_light += specular_brdf_NL * light_color;
+#endif
+}
+
+float get_omni_attenuation(float distance, float inv_range, float decay) {
+ float nd = distance * inv_range;
+ nd *= nd;
+ nd *= nd; // nd^4
+ nd = max(1.0 - nd, 0.0);
+ nd *= nd; // nd^2
+ return nd * pow(max(distance, 0.0001), -decay);
+}
+
+void light_process_omni_vertex(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, float roughness,
+ inout vec3 diffuse_light, inout vec3 specular_light) {
+ vec3 light_rel_vec = omni_lights.data[idx].position - vertex;
+ float light_length = length(light_rel_vec);
+ float omni_attenuation = get_omni_attenuation(light_length, omni_lights.data[idx].inv_radius, omni_lights.data[idx].attenuation);
+ vec3 color = omni_lights.data[idx].color * omni_attenuation;
+
+ light_compute_vertex(normal, normalize(light_rel_vec), eye_vec, color, false, roughness,
+ diffuse_light,
+ specular_light);
+}
+
+void light_process_spot_vertex(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, float roughness,
+ inout vec3 diffuse_light,
+ inout vec3 specular_light) {
+ vec3 light_rel_vec = spot_lights.data[idx].position - vertex;
+ float light_length = length(light_rel_vec);
+ float spot_attenuation = get_omni_attenuation(light_length, spot_lights.data[idx].inv_radius, spot_lights.data[idx].attenuation);
+ vec3 spot_dir = spot_lights.data[idx].direction;
+
+ // This conversion to a highp float is crucial to prevent light leaking
+ // due to precision errors in the following calculations (cone angle is mediump).
+ highp float cone_angle = spot_lights.data[idx].cone_angle;
+ float scos = max(dot(-normalize(light_rel_vec), spot_dir), cone_angle);
+ float spot_rim = max(0.0001, (1.0 - scos) / (1.0 - cone_angle));
+
+ spot_attenuation *= 1.0 - pow(spot_rim, spot_lights.data[idx].cone_attenuation);
+ vec3 color = spot_lights.data[idx].color * spot_attenuation;
+ float specular_amount = spot_lights.data[idx].specular_amount;
+
+ light_compute_vertex(normal, normalize(light_rel_vec), eye_vec, color, false, roughness,
+ diffuse_light, specular_light);
+}
diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp
index e5a8dbb9b2..84e8d1d671 100644
--- a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp
@@ -40,25 +40,12 @@ using namespace RendererRD;
///////////////////////////////////////////////////////////////////////////
// TextureStorage::CanvasTexture
-void TextureStorage::CanvasTexture::clear_sets() {
- if (cleared_cache) {
- return;
- }
- for (int i = 1; i < RS::CANVAS_ITEM_TEXTURE_FILTER_MAX; i++) {
- for (int j = 1; j < RS::CANVAS_ITEM_TEXTURE_REPEAT_MAX; j++) {
- for (int k = 0; k < 2; k++) {
- if (RD::get_singleton()->uniform_set_is_valid(uniform_sets[i][j][k])) {
- RD::get_singleton()->free(uniform_sets[i][j][k]);
- uniform_sets[i][j][k] = RID();
- }
- }
- }
- }
- cleared_cache = true;
+void TextureStorage::CanvasTexture::clear_cache() {
+ info_cache[0] = CanvasTextureCache();
+ info_cache[1] = CanvasTextureCache();
}
TextureStorage::CanvasTexture::~CanvasTexture() {
- clear_sets();
}
///////////////////////////////////////////////////////////////////////////
@@ -612,8 +599,7 @@ void TextureStorage::canvas_texture_set_channel(RID p_canvas_texture, RS::Canvas
ct->specular = p_texture;
} break;
}
-
- ct->clear_sets();
+ ct->clear_cache();
}
void TextureStorage::canvas_texture_set_shading_parameters(RID p_canvas_texture, const Color &p_specular_color, float p_shininess) {
@@ -624,7 +610,6 @@ void TextureStorage::canvas_texture_set_shading_parameters(RID p_canvas_texture,
ct->specular_color.g = p_specular_color.g;
ct->specular_color.b = p_specular_color.b;
ct->specular_color.a = p_shininess;
- ct->clear_sets();
}
void TextureStorage::canvas_texture_set_texture_filter(RID p_canvas_texture, RS::CanvasItemTextureFilter p_filter) {
@@ -632,7 +617,6 @@ void TextureStorage::canvas_texture_set_texture_filter(RID p_canvas_texture, RS:
ERR_FAIL_NULL(ct);
ct->texture_filter = p_filter;
- ct->clear_sets();
}
void TextureStorage::canvas_texture_set_texture_repeat(RID p_canvas_texture, RS::CanvasItemTextureRepeat p_repeat) {
@@ -640,17 +624,14 @@ void TextureStorage::canvas_texture_set_texture_repeat(RID p_canvas_texture, RS:
ERR_FAIL_NULL(ct);
ct->texture_repeat = p_repeat;
- ct->clear_sets();
}
-bool TextureStorage::canvas_texture_get_uniform_set(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID p_base_shader, int p_base_set, bool p_use_srgb, RID &r_uniform_set, Size2i &r_size, Color &r_specular_shininess, bool &r_use_normal, bool &r_use_specular, bool p_texture_is_data) {
+TextureStorage::CanvasTextureInfo TextureStorage::canvas_texture_get_info(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, bool p_use_srgb, bool p_texture_is_data) {
MaterialStorage *material_storage = MaterialStorage::get_singleton();
CanvasTexture *ct = nullptr;
Texture *t = get_texture(p_texture);
- // TODO once we have our texture storage split off we'll look into moving this code into canvas_texture
-
if (t) {
//regular texture
if (!t->canvas_texture) {
@@ -667,93 +648,71 @@ bool TextureStorage::canvas_texture_get_uniform_set(RID p_texture, RS::CanvasIte
}
if (!ct) {
- return false; //invalid texture RID
+ return CanvasTextureInfo(); //invalid texture RID
}
RS::CanvasItemTextureFilter filter = ct->texture_filter != RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT ? ct->texture_filter : p_base_filter;
- ERR_FAIL_COND_V(filter == RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, false);
+ ERR_FAIL_COND_V(filter == RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT, CanvasTextureInfo());
RS::CanvasItemTextureRepeat repeat = ct->texture_repeat != RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT ? ct->texture_repeat : p_base_repeat;
- ERR_FAIL_COND_V(repeat == RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT, false);
+ ERR_FAIL_COND_V(repeat == RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT, CanvasTextureInfo());
- RID uniform_set = ct->uniform_sets[filter][repeat][int(p_use_srgb)];
- if (!RD::get_singleton()->uniform_set_is_valid(uniform_set)) {
- //create and update
- Vector<RD::Uniform> uniforms;
+ CanvasTextureCache &ctc = ct->info_cache[int(p_use_srgb)];
+ if (!RD::get_singleton()->texture_is_valid(ctc.diffuse) ||
+ !RD::get_singleton()->texture_is_valid(ctc.normal) ||
+ !RD::get_singleton()->texture_is_valid(ctc.specular)) {
{ //diffuse
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- u.binding = 0;
-
t = get_texture(ct->diffuse);
if (!t) {
- u.append_id(texture_rd_get_default(DEFAULT_RD_TEXTURE_WHITE));
+ ctc.diffuse = texture_rd_get_default(DEFAULT_RD_TEXTURE_WHITE);
ct->size_cache = Size2i(1, 1);
} else {
- u.append_id(t->rd_texture_srgb.is_valid() && p_use_srgb && !p_texture_is_data ? t->rd_texture_srgb : t->rd_texture);
+ ctc.diffuse = t->rd_texture_srgb.is_valid() && p_use_srgb && !p_texture_is_data ? t->rd_texture_srgb : t->rd_texture;
ct->size_cache = Size2i(t->width_2d, t->height_2d);
if (t->render_target) {
t->render_target->was_used = true;
}
}
- uniforms.push_back(u);
}
{ //normal
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- u.binding = 1;
-
t = get_texture(ct->normal_map);
if (!t) {
- u.append_id(texture_rd_get_default(DEFAULT_RD_TEXTURE_NORMAL));
+ ctc.normal = texture_rd_get_default(DEFAULT_RD_TEXTURE_NORMAL);
ct->use_normal_cache = false;
} else {
- u.append_id(t->rd_texture);
+ ctc.normal = t->rd_texture;
ct->use_normal_cache = true;
if (t->render_target) {
t->render_target->was_used = true;
}
}
- uniforms.push_back(u);
}
{ //specular
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- u.binding = 2;
-
t = get_texture(ct->specular);
if (!t) {
- u.append_id(texture_rd_get_default(DEFAULT_RD_TEXTURE_WHITE));
+ ctc.specular = texture_rd_get_default(DEFAULT_RD_TEXTURE_WHITE);
ct->use_specular_cache = false;
} else {
- u.append_id(t->rd_texture);
+ ctc.specular = t->rd_texture;
ct->use_specular_cache = true;
if (t->render_target) {
t->render_target->was_used = true;
}
}
- uniforms.push_back(u);
}
- { //sampler
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_SAMPLER;
- u.binding = 3;
- u.append_id(material_storage->sampler_rd_get_default(filter, repeat));
- uniforms.push_back(u);
- }
-
- uniform_set = RD::get_singleton()->uniform_set_create(uniforms, p_base_shader, p_base_set);
- ct->uniform_sets[filter][repeat][int(p_use_srgb)] = uniform_set;
- ct->cleared_cache = false;
}
- r_uniform_set = uniform_set;
- r_size = ct->size_cache;
- r_specular_shininess = ct->specular_color;
- r_use_normal = ct->use_normal_cache;
- r_use_specular = ct->use_specular_cache;
+ CanvasTextureInfo res;
+ res.diffuse = ctc.diffuse;
+ res.normal = ctc.normal;
+ res.specular = ctc.specular;
+ res.sampler = material_storage->sampler_rd_get_default(filter, repeat);
+ res.size = ct->size_cache;
+ res.specular_color = ct->specular_color;
+ res.use_normal = ct->use_normal_cache;
+ res.use_specular = ct->use_specular_cache;
- return true;
+ return res;
}
/* Texture API */
diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.h b/servers/rendering/renderer_rd/storage_rd/texture_storage.h
index 9de4ff7b6b..8aecc78f07 100644
--- a/servers/rendering/renderer_rd/storage_rd/texture_storage.h
+++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.h
@@ -76,6 +76,21 @@ public:
TYPE_3D
};
+ struct CanvasTextureInfo {
+ RID diffuse;
+ RID normal;
+ RID specular;
+ RID sampler;
+ Size2i size;
+ Color specular_color;
+
+ bool use_normal = false;
+ bool use_specular = false;
+
+ _FORCE_INLINE_ bool is_valid() const { return diffuse.is_valid(); }
+ _FORCE_INLINE_ bool is_null() const { return diffuse.is_null(); }
+ };
+
private:
friend class LightStorage;
friend class MaterialStorage;
@@ -86,6 +101,12 @@ private:
/* Canvas Texture API */
+ struct CanvasTextureCache {
+ RID diffuse = RID();
+ RID normal = RID();
+ RID specular = RID();
+ };
+
class CanvasTexture {
public:
RID diffuse;
@@ -96,14 +117,14 @@ private:
RS::CanvasItemTextureFilter texture_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT;
RS::CanvasItemTextureRepeat texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT;
- RID uniform_sets[RS::CANVAS_ITEM_TEXTURE_FILTER_MAX][RS::CANVAS_ITEM_TEXTURE_REPEAT_MAX][2];
+ CanvasTextureCache info_cache[2];
Size2i size_cache = Size2i(1, 1);
bool use_normal_cache = false;
bool use_specular_cache = false;
bool cleared_cache = true;
- void clear_sets();
+ void clear_cache();
~CanvasTexture();
};
@@ -477,7 +498,7 @@ public:
virtual void canvas_texture_set_texture_filter(RID p_item, RS::CanvasItemTextureFilter p_filter) override;
virtual void canvas_texture_set_texture_repeat(RID p_item, RS::CanvasItemTextureRepeat p_repeat) override;
- bool canvas_texture_get_uniform_set(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID p_base_shader, int p_base_set, bool p_use_srgb, RID &r_uniform_set, Size2i &r_size, Color &r_specular_shininess, bool &r_use_normal, bool &r_use_specular, bool p_texture_is_data);
+ CanvasTextureInfo canvas_texture_get_info(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, bool p_use_srgb, bool p_texture_is_data);
/* Texture API */
diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp
index 0ad56961c0..5c85080298 100644
--- a/servers/rendering_server.cpp
+++ b/servers/rendering_server.cpp
@@ -3580,8 +3580,7 @@ void RenderingServer::init() {
GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/global_illumination/voxel_gi/quality", PROPERTY_HINT_ENUM, "Low (4 Cones - Fast),High (6 Cones - Slow)"), 0);
- GLOBAL_DEF("rendering/shading/overrides/force_vertex_shading", false);
- GLOBAL_DEF("rendering/shading/overrides/force_vertex_shading.mobile", true);
+ GLOBAL_DEF_RST("rendering/shading/overrides/force_vertex_shading", false);
GLOBAL_DEF("rendering/shading/overrides/force_lambert_over_burley", false);
GLOBAL_DEF("rendering/shading/overrides/force_lambert_over_burley.mobile", true);
diff --git a/servers/text/text_server_extension.cpp b/servers/text/text_server_extension.cpp
index d387c8ff7e..1c0d518e75 100644
--- a/servers/text/text_server_extension.cpp
+++ b/servers/text/text_server_extension.cpp
@@ -352,29 +352,29 @@ void TextServerExtension::_bind_methods() {
bool TextServerExtension::has_feature(Feature p_feature) const {
bool ret = false;
- GDVIRTUAL_REQUIRED_CALL(_has_feature, p_feature, ret);
+ GDVIRTUAL_CALL(_has_feature, p_feature, ret);
return ret;
}
String TextServerExtension::get_name() const {
String ret = "Unknown";
- GDVIRTUAL_REQUIRED_CALL(_get_name, ret);
+ GDVIRTUAL_CALL(_get_name, ret);
return ret;
}
int64_t TextServerExtension::get_features() const {
int64_t ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_get_features, ret);
+ GDVIRTUAL_CALL(_get_features, ret);
return ret;
}
void TextServerExtension::free_rid(const RID &p_rid) {
- GDVIRTUAL_REQUIRED_CALL(_free_rid, p_rid);
+ GDVIRTUAL_CALL(_free_rid, p_rid);
}
bool TextServerExtension::has(const RID &p_rid) {
bool ret = false;
- GDVIRTUAL_REQUIRED_CALL(_has, p_rid, ret);
+ GDVIRTUAL_CALL(_has, p_rid, ret);
return ret;
}
@@ -430,7 +430,7 @@ String TextServerExtension::tag_to_name(int64_t p_tag) const {
RID TextServerExtension::create_font() {
RID ret;
- GDVIRTUAL_REQUIRED_CALL(_create_font, ret);
+ GDVIRTUAL_CALL(_create_font, ret);
return ret;
}
@@ -581,22 +581,22 @@ int64_t TextServerExtension::font_get_msdf_size(const RID &p_font_rid) const {
}
void TextServerExtension::font_set_fixed_size(const RID &p_font_rid, int64_t p_fixed_size) {
- GDVIRTUAL_REQUIRED_CALL(_font_set_fixed_size, p_font_rid, p_fixed_size);
+ GDVIRTUAL_CALL(_font_set_fixed_size, p_font_rid, p_fixed_size);
}
int64_t TextServerExtension::font_get_fixed_size(const RID &p_font_rid) const {
int64_t ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_font_get_fixed_size, p_font_rid, ret);
+ GDVIRTUAL_CALL(_font_get_fixed_size, p_font_rid, ret);
return ret;
}
void TextServerExtension::font_set_fixed_size_scale_mode(const RID &p_font_rid, TextServer::FixedSizeScaleMode p_fixed_size_scale_mode) {
- GDVIRTUAL_REQUIRED_CALL(_font_set_fixed_size_scale_mode, p_font_rid, p_fixed_size_scale_mode);
+ GDVIRTUAL_CALL(_font_set_fixed_size_scale_mode, p_font_rid, p_fixed_size_scale_mode);
}
TextServer::FixedSizeScaleMode TextServerExtension::font_get_fixed_size_scale_mode(const RID &p_font_rid) const {
FixedSizeScaleMode ret = FIXED_SIZE_SCALE_DISABLE;
- GDVIRTUAL_REQUIRED_CALL(_font_get_fixed_size_scale_mode, p_font_rid, ret);
+ GDVIRTUAL_CALL(_font_get_fixed_size_scale_mode, p_font_rid, ret);
return ret;
}
@@ -702,89 +702,89 @@ double TextServerExtension::font_get_oversampling(const RID &p_font_rid) const {
TypedArray<Vector2i> TextServerExtension::font_get_size_cache_list(const RID &p_font_rid) const {
TypedArray<Vector2i> ret;
- GDVIRTUAL_REQUIRED_CALL(_font_get_size_cache_list, p_font_rid, ret);
+ GDVIRTUAL_CALL(_font_get_size_cache_list, p_font_rid, ret);
return ret;
}
void TextServerExtension::font_clear_size_cache(const RID &p_font_rid) {
- GDVIRTUAL_REQUIRED_CALL(_font_clear_size_cache, p_font_rid);
+ GDVIRTUAL_CALL(_font_clear_size_cache, p_font_rid);
}
void TextServerExtension::font_remove_size_cache(const RID &p_font_rid, const Vector2i &p_size) {
- GDVIRTUAL_REQUIRED_CALL(_font_remove_size_cache, p_font_rid, p_size);
+ GDVIRTUAL_CALL(_font_remove_size_cache, p_font_rid, p_size);
}
void TextServerExtension::font_set_ascent(const RID &p_font_rid, int64_t p_size, double p_ascent) {
- GDVIRTUAL_REQUIRED_CALL(_font_set_ascent, p_font_rid, p_size, p_ascent);
+ GDVIRTUAL_CALL(_font_set_ascent, p_font_rid, p_size, p_ascent);
}
double TextServerExtension::font_get_ascent(const RID &p_font_rid, int64_t p_size) const {
double ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_font_get_ascent, p_font_rid, p_size, ret);
+ GDVIRTUAL_CALL(_font_get_ascent, p_font_rid, p_size, ret);
return ret;
}
void TextServerExtension::font_set_descent(const RID &p_font_rid, int64_t p_size, double p_descent) {
- GDVIRTUAL_REQUIRED_CALL(_font_set_descent, p_font_rid, p_size, p_descent);
+ GDVIRTUAL_CALL(_font_set_descent, p_font_rid, p_size, p_descent);
}
double TextServerExtension::font_get_descent(const RID &p_font_rid, int64_t p_size) const {
double ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_font_get_descent, p_font_rid, p_size, ret);
+ GDVIRTUAL_CALL(_font_get_descent, p_font_rid, p_size, ret);
return ret;
}
void TextServerExtension::font_set_underline_position(const RID &p_font_rid, int64_t p_size, double p_underline_position) {
- GDVIRTUAL_REQUIRED_CALL(_font_set_underline_position, p_font_rid, p_size, p_underline_position);
+ GDVIRTUAL_CALL(_font_set_underline_position, p_font_rid, p_size, p_underline_position);
}
double TextServerExtension::font_get_underline_position(const RID &p_font_rid, int64_t p_size) const {
double ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_font_get_underline_position, p_font_rid, p_size, ret);
+ GDVIRTUAL_CALL(_font_get_underline_position, p_font_rid, p_size, ret);
return ret;
}
void TextServerExtension::font_set_underline_thickness(const RID &p_font_rid, int64_t p_size, double p_underline_thickness) {
- GDVIRTUAL_REQUIRED_CALL(_font_set_underline_thickness, p_font_rid, p_size, p_underline_thickness);
+ GDVIRTUAL_CALL(_font_set_underline_thickness, p_font_rid, p_size, p_underline_thickness);
}
double TextServerExtension::font_get_underline_thickness(const RID &p_font_rid, int64_t p_size) const {
double ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_font_get_underline_thickness, p_font_rid, p_size, ret);
+ GDVIRTUAL_CALL(_font_get_underline_thickness, p_font_rid, p_size, ret);
return ret;
}
void TextServerExtension::font_set_scale(const RID &p_font_rid, int64_t p_size, double p_scale) {
- GDVIRTUAL_REQUIRED_CALL(_font_set_scale, p_font_rid, p_size, p_scale);
+ GDVIRTUAL_CALL(_font_set_scale, p_font_rid, p_size, p_scale);
}
double TextServerExtension::font_get_scale(const RID &p_font_rid, int64_t p_size) const {
double ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_font_get_scale, p_font_rid, p_size, ret);
+ GDVIRTUAL_CALL(_font_get_scale, p_font_rid, p_size, ret);
return ret;
}
int64_t TextServerExtension::font_get_texture_count(const RID &p_font_rid, const Vector2i &p_size) const {
int64_t ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_font_get_texture_count, p_font_rid, p_size, ret);
+ GDVIRTUAL_CALL(_font_get_texture_count, p_font_rid, p_size, ret);
return ret;
}
void TextServerExtension::font_clear_textures(const RID &p_font_rid, const Vector2i &p_size) {
- GDVIRTUAL_REQUIRED_CALL(_font_clear_textures, p_font_rid, p_size);
+ GDVIRTUAL_CALL(_font_clear_textures, p_font_rid, p_size);
}
void TextServerExtension::font_remove_texture(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) {
- GDVIRTUAL_REQUIRED_CALL(_font_remove_texture, p_font_rid, p_size, p_texture_index);
+ GDVIRTUAL_CALL(_font_remove_texture, p_font_rid, p_size, p_texture_index);
}
void TextServerExtension::font_set_texture_image(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index, const Ref<Image> &p_image) {
- GDVIRTUAL_REQUIRED_CALL(_font_set_texture_image, p_font_rid, p_size, p_texture_index, p_image);
+ GDVIRTUAL_CALL(_font_set_texture_image, p_font_rid, p_size, p_texture_index, p_image);
}
Ref<Image> TextServerExtension::font_get_texture_image(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) const {
Ref<Image> ret;
- GDVIRTUAL_REQUIRED_CALL(_font_get_texture_image, p_font_rid, p_size, p_texture_index, ret);
+ GDVIRTUAL_CALL(_font_get_texture_image, p_font_rid, p_size, p_texture_index, ret);
return ret;
}
@@ -800,77 +800,77 @@ PackedInt32Array TextServerExtension::font_get_texture_offsets(const RID &p_font
PackedInt32Array TextServerExtension::font_get_glyph_list(const RID &p_font_rid, const Vector2i &p_size) const {
PackedInt32Array ret;
- GDVIRTUAL_REQUIRED_CALL(_font_get_glyph_list, p_font_rid, p_size, ret);
+ GDVIRTUAL_CALL(_font_get_glyph_list, p_font_rid, p_size, ret);
return ret;
}
void TextServerExtension::font_clear_glyphs(const RID &p_font_rid, const Vector2i &p_size) {
- GDVIRTUAL_REQUIRED_CALL(_font_clear_glyphs, p_font_rid, p_size);
+ GDVIRTUAL_CALL(_font_clear_glyphs, p_font_rid, p_size);
}
void TextServerExtension::font_remove_glyph(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) {
- GDVIRTUAL_REQUIRED_CALL(_font_remove_glyph, p_font_rid, p_size, p_glyph);
+ GDVIRTUAL_CALL(_font_remove_glyph, p_font_rid, p_size, p_glyph);
}
Vector2 TextServerExtension::font_get_glyph_advance(const RID &p_font_rid, int64_t p_size, int64_t p_glyph) const {
Vector2 ret;
- GDVIRTUAL_REQUIRED_CALL(_font_get_glyph_advance, p_font_rid, p_size, p_glyph, ret);
+ GDVIRTUAL_CALL(_font_get_glyph_advance, p_font_rid, p_size, p_glyph, ret);
return ret;
}
void TextServerExtension::font_set_glyph_advance(const RID &p_font_rid, int64_t p_size, int64_t p_glyph, const Vector2 &p_advance) {
- GDVIRTUAL_REQUIRED_CALL(_font_set_glyph_advance, p_font_rid, p_size, p_glyph, p_advance);
+ GDVIRTUAL_CALL(_font_set_glyph_advance, p_font_rid, p_size, p_glyph, p_advance);
}
Vector2 TextServerExtension::font_get_glyph_offset(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
Vector2 ret;
- GDVIRTUAL_REQUIRED_CALL(_font_get_glyph_offset, p_font_rid, p_size, p_glyph, ret);
+ GDVIRTUAL_CALL(_font_get_glyph_offset, p_font_rid, p_size, p_glyph, ret);
return ret;
}
void TextServerExtension::font_set_glyph_offset(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Vector2 &p_offset) {
- GDVIRTUAL_REQUIRED_CALL(_font_set_glyph_offset, p_font_rid, p_size, p_glyph, p_offset);
+ GDVIRTUAL_CALL(_font_set_glyph_offset, p_font_rid, p_size, p_glyph, p_offset);
}
Vector2 TextServerExtension::font_get_glyph_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
Vector2 ret;
- GDVIRTUAL_REQUIRED_CALL(_font_get_glyph_size, p_font_rid, p_size, p_glyph, ret);
+ GDVIRTUAL_CALL(_font_get_glyph_size, p_font_rid, p_size, p_glyph, ret);
return ret;
}
void TextServerExtension::font_set_glyph_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Vector2 &p_gl_size) {
- GDVIRTUAL_REQUIRED_CALL(_font_set_glyph_size, p_font_rid, p_size, p_glyph, p_gl_size);
+ GDVIRTUAL_CALL(_font_set_glyph_size, p_font_rid, p_size, p_glyph, p_gl_size);
}
Rect2 TextServerExtension::font_get_glyph_uv_rect(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
Rect2 ret;
- GDVIRTUAL_REQUIRED_CALL(_font_get_glyph_uv_rect, p_font_rid, p_size, p_glyph, ret);
+ GDVIRTUAL_CALL(_font_get_glyph_uv_rect, p_font_rid, p_size, p_glyph, ret);
return ret;
}
void TextServerExtension::font_set_glyph_uv_rect(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Rect2 &p_uv_rect) {
- GDVIRTUAL_REQUIRED_CALL(_font_set_glyph_uv_rect, p_font_rid, p_size, p_glyph, p_uv_rect);
+ GDVIRTUAL_CALL(_font_set_glyph_uv_rect, p_font_rid, p_size, p_glyph, p_uv_rect);
}
int64_t TextServerExtension::font_get_glyph_texture_idx(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
int64_t ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_font_get_glyph_texture_idx, p_font_rid, p_size, p_glyph, ret);
+ GDVIRTUAL_CALL(_font_get_glyph_texture_idx, p_font_rid, p_size, p_glyph, ret);
return ret;
}
void TextServerExtension::font_set_glyph_texture_idx(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, int64_t p_texture_idx) {
- GDVIRTUAL_REQUIRED_CALL(_font_set_glyph_texture_idx, p_font_rid, p_size, p_glyph, p_texture_idx);
+ GDVIRTUAL_CALL(_font_set_glyph_texture_idx, p_font_rid, p_size, p_glyph, p_texture_idx);
}
RID TextServerExtension::font_get_glyph_texture_rid(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
RID ret;
- GDVIRTUAL_REQUIRED_CALL(_font_get_glyph_texture_rid, p_font_rid, p_size, p_glyph, ret);
+ GDVIRTUAL_CALL(_font_get_glyph_texture_rid, p_font_rid, p_size, p_glyph, ret);
return ret;
}
Size2 TextServerExtension::font_get_glyph_texture_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const {
Size2 ret;
- GDVIRTUAL_REQUIRED_CALL(_font_get_glyph_texture_size, p_font_rid, p_size, p_glyph, ret);
+ GDVIRTUAL_CALL(_font_get_glyph_texture_size, p_font_rid, p_size, p_glyph, ret);
return ret;
}
@@ -906,31 +906,31 @@ Vector2 TextServerExtension::font_get_kerning(const RID &p_font_rid, int64_t p_s
int64_t TextServerExtension::font_get_glyph_index(const RID &p_font_rid, int64_t p_size, int64_t p_char, int64_t p_variation_selector) const {
int64_t ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_font_get_glyph_index, p_font_rid, p_size, p_char, p_variation_selector, ret);
+ GDVIRTUAL_CALL(_font_get_glyph_index, p_font_rid, p_size, p_char, p_variation_selector, ret);
return ret;
}
int64_t TextServerExtension::font_get_char_from_glyph_index(const RID &p_font_rid, int64_t p_size, int64_t p_glyph_index) const {
int64_t ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_font_get_char_from_glyph_index, p_font_rid, p_size, p_glyph_index, ret);
+ GDVIRTUAL_CALL(_font_get_char_from_glyph_index, p_font_rid, p_size, p_glyph_index, ret);
return ret;
}
bool TextServerExtension::font_has_char(const RID &p_font_rid, int64_t p_char) const {
bool ret = false;
- GDVIRTUAL_REQUIRED_CALL(_font_has_char, p_font_rid, p_char, ret);
+ GDVIRTUAL_CALL(_font_has_char, p_font_rid, p_char, ret);
return ret;
}
String TextServerExtension::font_get_supported_chars(const RID &p_font_rid) const {
String ret;
- GDVIRTUAL_REQUIRED_CALL(_font_get_supported_chars, p_font_rid, ret);
+ GDVIRTUAL_CALL(_font_get_supported_chars, p_font_rid, ret);
return ret;
}
PackedInt32Array TextServerExtension::font_get_supported_glyphs(const RID &p_font_rid) const {
PackedInt32Array ret;
- GDVIRTUAL_REQUIRED_CALL(_font_get_supported_glyphs, p_font_rid, ret);
+ GDVIRTUAL_CALL(_font_get_supported_glyphs, p_font_rid, ret);
return ret;
}
@@ -943,11 +943,11 @@ void TextServerExtension::font_render_glyph(const RID &p_font_rid, const Vector2
}
void TextServerExtension::font_draw_glyph(const RID &p_font_rid, const RID &p_canvas, int64_t p_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color) const {
- GDVIRTUAL_REQUIRED_CALL(_font_draw_glyph, p_font_rid, p_canvas, p_size, p_pos, p_index, p_color);
+ GDVIRTUAL_CALL(_font_draw_glyph, p_font_rid, p_canvas, p_size, p_pos, p_index, p_color);
}
void TextServerExtension::font_draw_glyph_outline(const RID &p_font_rid, const RID &p_canvas, int64_t p_size, int64_t p_outline_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color) const {
- GDVIRTUAL_REQUIRED_CALL(_font_draw_glyph_outline, p_font_rid, p_canvas, p_size, p_outline_size, p_pos, p_index, p_color);
+ GDVIRTUAL_CALL(_font_draw_glyph_outline, p_font_rid, p_canvas, p_size, p_outline_size, p_pos, p_index, p_color);
}
bool TextServerExtension::font_is_language_supported(const RID &p_font_rid, const String &p_language) const {
@@ -1054,12 +1054,12 @@ void TextServerExtension::draw_hex_code_box(const RID &p_canvas, int64_t p_size,
RID TextServerExtension::create_shaped_text(TextServer::Direction p_direction, TextServer::Orientation p_orientation) {
RID ret;
- GDVIRTUAL_REQUIRED_CALL(_create_shaped_text, p_direction, p_orientation, ret);
+ GDVIRTUAL_CALL(_create_shaped_text, p_direction, p_orientation, ret);
return ret;
}
void TextServerExtension::shaped_text_clear(const RID &p_shaped) {
- GDVIRTUAL_REQUIRED_CALL(_shaped_text_clear, p_shaped);
+ GDVIRTUAL_CALL(_shaped_text_clear, p_shaped);
}
void TextServerExtension::shaped_text_set_direction(const RID &p_shaped, TextServer::Direction p_direction) {
@@ -1144,47 +1144,47 @@ int64_t TextServerExtension::shaped_text_get_spacing(const RID &p_shaped, TextSe
bool TextServerExtension::shaped_text_add_string(const RID &p_shaped, const String &p_text, const TypedArray<RID> &p_fonts, int64_t p_size, const Dictionary &p_opentype_features, const String &p_language, const Variant &p_meta) {
bool ret = false;
- GDVIRTUAL_REQUIRED_CALL(_shaped_text_add_string, p_shaped, p_text, p_fonts, p_size, p_opentype_features, p_language, p_meta, ret);
+ GDVIRTUAL_CALL(_shaped_text_add_string, p_shaped, p_text, p_fonts, p_size, p_opentype_features, p_language, p_meta, ret);
return ret;
}
bool TextServerExtension::shaped_text_add_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align, int64_t p_length, double p_baseline) {
bool ret = false;
- GDVIRTUAL_REQUIRED_CALL(_shaped_text_add_object, p_shaped, p_key, p_size, p_inline_align, p_length, p_baseline, ret);
+ GDVIRTUAL_CALL(_shaped_text_add_object, p_shaped, p_key, p_size, p_inline_align, p_length, p_baseline, ret);
return ret;
}
bool TextServerExtension::shaped_text_resize_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align, double p_baseline) {
bool ret = false;
- GDVIRTUAL_REQUIRED_CALL(_shaped_text_resize_object, p_shaped, p_key, p_size, p_inline_align, p_baseline, ret);
+ GDVIRTUAL_CALL(_shaped_text_resize_object, p_shaped, p_key, p_size, p_inline_align, p_baseline, ret);
return ret;
}
int64_t TextServerExtension::shaped_get_span_count(const RID &p_shaped) const {
int64_t ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_shaped_get_span_count, p_shaped, ret);
+ GDVIRTUAL_CALL(_shaped_get_span_count, p_shaped, ret);
return ret;
}
Variant TextServerExtension::shaped_get_span_meta(const RID &p_shaped, int64_t p_index) const {
Variant ret = false;
- GDVIRTUAL_REQUIRED_CALL(_shaped_get_span_meta, p_shaped, p_index, ret);
+ GDVIRTUAL_CALL(_shaped_get_span_meta, p_shaped, p_index, ret);
return ret;
}
void TextServerExtension::shaped_set_span_update_font(const RID &p_shaped, int64_t p_index, const TypedArray<RID> &p_fonts, int64_t p_size, const Dictionary &p_opentype_features) {
- GDVIRTUAL_REQUIRED_CALL(_shaped_set_span_update_font, p_shaped, p_index, p_fonts, p_size, p_opentype_features);
+ GDVIRTUAL_CALL(_shaped_set_span_update_font, p_shaped, p_index, p_fonts, p_size, p_opentype_features);
}
RID TextServerExtension::shaped_text_substr(const RID &p_shaped, int64_t p_start, int64_t p_length) const {
RID ret;
- GDVIRTUAL_REQUIRED_CALL(_shaped_text_substr, p_shaped, p_start, p_length, ret);
+ GDVIRTUAL_CALL(_shaped_text_substr, p_shaped, p_start, p_length, ret);
return ret;
}
RID TextServerExtension::shaped_text_get_parent(const RID &p_shaped) const {
RID ret;
- GDVIRTUAL_REQUIRED_CALL(_shaped_text_get_parent, p_shaped, ret);
+ GDVIRTUAL_CALL(_shaped_text_get_parent, p_shaped, ret);
return ret;
}
@@ -1202,7 +1202,7 @@ double TextServerExtension::shaped_text_tab_align(const RID &p_shaped, const Pac
bool TextServerExtension::shaped_text_shape(const RID &p_shaped) {
bool ret = false;
- GDVIRTUAL_REQUIRED_CALL(_shaped_text_shape, p_shaped, ret);
+ GDVIRTUAL_CALL(_shaped_text_shape, p_shaped, ret);
return ret;
}
@@ -1220,31 +1220,31 @@ bool TextServerExtension::shaped_text_update_justification_ops(const RID &p_shap
bool TextServerExtension::shaped_text_is_ready(const RID &p_shaped) const {
bool ret = false;
- GDVIRTUAL_REQUIRED_CALL(_shaped_text_is_ready, p_shaped, ret);
+ GDVIRTUAL_CALL(_shaped_text_is_ready, p_shaped, ret);
return ret;
}
const Glyph *TextServerExtension::shaped_text_get_glyphs(const RID &p_shaped) const {
GDExtensionConstPtr<const Glyph> ret;
- GDVIRTUAL_REQUIRED_CALL(_shaped_text_get_glyphs, p_shaped, ret);
+ GDVIRTUAL_CALL(_shaped_text_get_glyphs, p_shaped, ret);
return ret;
}
const Glyph *TextServerExtension::shaped_text_sort_logical(const RID &p_shaped) {
GDExtensionConstPtr<const Glyph> ret;
- GDVIRTUAL_REQUIRED_CALL(_shaped_text_sort_logical, p_shaped, ret);
+ GDVIRTUAL_CALL(_shaped_text_sort_logical, p_shaped, ret);
return ret;
}
int64_t TextServerExtension::shaped_text_get_glyph_count(const RID &p_shaped) const {
int64_t ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_shaped_text_get_glyph_count, p_shaped, ret);
+ GDVIRTUAL_CALL(_shaped_text_get_glyph_count, p_shaped, ret);
return ret;
}
Vector2i TextServerExtension::shaped_text_get_range(const RID &p_shaped) const {
Vector2i ret;
- GDVIRTUAL_REQUIRED_CALL(_shaped_text_get_range, p_shaped, ret);
+ GDVIRTUAL_CALL(_shaped_text_get_range, p_shaped, ret);
return ret;
}
@@ -1274,25 +1274,25 @@ PackedInt32Array TextServerExtension::shaped_text_get_word_breaks(const RID &p_s
int64_t TextServerExtension::shaped_text_get_trim_pos(const RID &p_shaped) const {
int64_t ret = -1;
- GDVIRTUAL_REQUIRED_CALL(_shaped_text_get_trim_pos, p_shaped, ret);
+ GDVIRTUAL_CALL(_shaped_text_get_trim_pos, p_shaped, ret);
return ret;
}
int64_t TextServerExtension::shaped_text_get_ellipsis_pos(const RID &p_shaped) const {
int64_t ret = -1;
- GDVIRTUAL_REQUIRED_CALL(_shaped_text_get_ellipsis_pos, p_shaped, ret);
+ GDVIRTUAL_CALL(_shaped_text_get_ellipsis_pos, p_shaped, ret);
return ret;
}
const Glyph *TextServerExtension::shaped_text_get_ellipsis_glyphs(const RID &p_shaped) const {
GDExtensionConstPtr<const Glyph> ret;
- GDVIRTUAL_REQUIRED_CALL(_shaped_text_get_ellipsis_glyphs, p_shaped, ret);
+ GDVIRTUAL_CALL(_shaped_text_get_ellipsis_glyphs, p_shaped, ret);
return ret;
}
int64_t TextServerExtension::shaped_text_get_ellipsis_glyph_count(const RID &p_shaped) const {
int64_t ret = -1;
- GDVIRTUAL_REQUIRED_CALL(_shaped_text_get_ellipsis_glyph_count, p_shaped, ret);
+ GDVIRTUAL_CALL(_shaped_text_get_ellipsis_glyph_count, p_shaped, ret);
return ret;
}
@@ -1302,61 +1302,61 @@ void TextServerExtension::shaped_text_overrun_trim_to_width(const RID &p_shaped_
Array TextServerExtension::shaped_text_get_objects(const RID &p_shaped) const {
Array ret;
- GDVIRTUAL_REQUIRED_CALL(_shaped_text_get_objects, p_shaped, ret);
+ GDVIRTUAL_CALL(_shaped_text_get_objects, p_shaped, ret);
return ret;
}
Rect2 TextServerExtension::shaped_text_get_object_rect(const RID &p_shaped, const Variant &p_key) const {
Rect2 ret;
- GDVIRTUAL_REQUIRED_CALL(_shaped_text_get_object_rect, p_shaped, p_key, ret);
+ GDVIRTUAL_CALL(_shaped_text_get_object_rect, p_shaped, p_key, ret);
return ret;
}
Vector2i TextServerExtension::shaped_text_get_object_range(const RID &p_shaped, const Variant &p_key) const {
Vector2i ret;
- GDVIRTUAL_REQUIRED_CALL(_shaped_text_get_object_range, p_shaped, p_key, ret);
+ GDVIRTUAL_CALL(_shaped_text_get_object_range, p_shaped, p_key, ret);
return ret;
}
int64_t TextServerExtension::shaped_text_get_object_glyph(const RID &p_shaped, const Variant &p_key) const {
int64_t ret = -1;
- GDVIRTUAL_REQUIRED_CALL(_shaped_text_get_object_glyph, p_shaped, p_key, ret);
+ GDVIRTUAL_CALL(_shaped_text_get_object_glyph, p_shaped, p_key, ret);
return ret;
}
Size2 TextServerExtension::shaped_text_get_size(const RID &p_shaped) const {
Size2 ret;
- GDVIRTUAL_REQUIRED_CALL(_shaped_text_get_size, p_shaped, ret);
+ GDVIRTUAL_CALL(_shaped_text_get_size, p_shaped, ret);
return ret;
}
double TextServerExtension::shaped_text_get_ascent(const RID &p_shaped) const {
double ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_shaped_text_get_ascent, p_shaped, ret);
+ GDVIRTUAL_CALL(_shaped_text_get_ascent, p_shaped, ret);
return ret;
}
double TextServerExtension::shaped_text_get_descent(const RID &p_shaped) const {
double ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_shaped_text_get_descent, p_shaped, ret);
+ GDVIRTUAL_CALL(_shaped_text_get_descent, p_shaped, ret);
return ret;
}
double TextServerExtension::shaped_text_get_width(const RID &p_shaped) const {
double ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_shaped_text_get_width, p_shaped, ret);
+ GDVIRTUAL_CALL(_shaped_text_get_width, p_shaped, ret);
return ret;
}
double TextServerExtension::shaped_text_get_underline_position(const RID &p_shaped) const {
double ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_shaped_text_get_underline_position, p_shaped, ret);
+ GDVIRTUAL_CALL(_shaped_text_get_underline_position, p_shaped, ret);
return ret;
}
double TextServerExtension::shaped_text_get_underline_thickness(const RID &p_shaped) const {
double ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_shaped_text_get_underline_thickness, p_shaped, ret);
+ GDVIRTUAL_CALL(_shaped_text_get_underline_thickness, p_shaped, ret);
return ret;
}
diff --git a/servers/text/text_server_extension.h b/servers/text/text_server_extension.h
index 52654c010c..bd803be8aa 100644
--- a/servers/text/text_server_extension.h
+++ b/servers/text/text_server_extension.h
@@ -49,15 +49,15 @@ public:
virtual bool has_feature(Feature p_feature) const override;
virtual String get_name() const override;
virtual int64_t get_features() const override;
- GDVIRTUAL1RC(bool, _has_feature, Feature);
- GDVIRTUAL0RC(String, _get_name);
- GDVIRTUAL0RC(int64_t, _get_features);
+ GDVIRTUAL1RC_REQUIRED(bool, _has_feature, Feature);
+ GDVIRTUAL0RC_REQUIRED(String, _get_name);
+ GDVIRTUAL0RC_REQUIRED(int64_t, _get_features);
virtual void free_rid(const RID &p_rid) override;
virtual bool has(const RID &p_rid) override;
virtual bool load_support_data(const String &p_filename) override;
- GDVIRTUAL1(_free_rid, RID);
- GDVIRTUAL1R(bool, _has, RID);
+ GDVIRTUAL1_REQUIRED(_free_rid, RID);
+ GDVIRTUAL1R_REQUIRED(bool, _has, RID);
GDVIRTUAL1R(bool, _load_support_data, const String &);
virtual String get_support_data_filename() const override;
@@ -78,7 +78,7 @@ public:
/* Font interface */
virtual RID create_font() override;
- GDVIRTUAL0R(RID, _create_font);
+ GDVIRTUAL0R_REQUIRED(RID, _create_font);
virtual RID create_font_linked_variation(const RID &p_font_rid) override;
GDVIRTUAL1R(RID, _create_font_linked_variation, RID);
@@ -155,13 +155,13 @@ public:
virtual void font_set_fixed_size(const RID &p_font_rid, int64_t p_fixed_size) override;
virtual int64_t font_get_fixed_size(const RID &p_font_rid) const override;
- GDVIRTUAL2(_font_set_fixed_size, RID, int64_t);
- GDVIRTUAL1RC(int64_t, _font_get_fixed_size, RID);
+ GDVIRTUAL2_REQUIRED(_font_set_fixed_size, RID, int64_t);
+ GDVIRTUAL1RC_REQUIRED(int64_t, _font_get_fixed_size, RID);
virtual void font_set_fixed_size_scale_mode(const RID &p_font_rid, FixedSizeScaleMode p_fixed_size_scale) override;
virtual FixedSizeScaleMode font_get_fixed_size_scale_mode(const RID &p_font_rid) const override;
- GDVIRTUAL2(_font_set_fixed_size_scale_mode, RID, FixedSizeScaleMode);
- GDVIRTUAL1RC(FixedSizeScaleMode, _font_get_fixed_size_scale_mode, RID);
+ GDVIRTUAL2_REQUIRED(_font_set_fixed_size_scale_mode, RID, FixedSizeScaleMode);
+ GDVIRTUAL1RC_REQUIRED(FixedSizeScaleMode, _font_get_fixed_size_scale_mode, RID);
virtual void font_set_subpixel_positioning(const RID &p_font_rid, SubpixelPositioning p_subpixel) override;
virtual SubpixelPositioning font_get_subpixel_positioning(const RID &p_font_rid) const override;
@@ -216,46 +216,46 @@ public:
virtual TypedArray<Vector2i> font_get_size_cache_list(const RID &p_font_rid) const override;
virtual void font_clear_size_cache(const RID &p_font_rid) override;
virtual void font_remove_size_cache(const RID &p_font_rid, const Vector2i &p_size) override;
- GDVIRTUAL1RC(TypedArray<Vector2i>, _font_get_size_cache_list, RID);
- GDVIRTUAL1(_font_clear_size_cache, RID);
- GDVIRTUAL2(_font_remove_size_cache, RID, const Vector2i &);
+ GDVIRTUAL1RC_REQUIRED(TypedArray<Vector2i>, _font_get_size_cache_list, RID);
+ GDVIRTUAL1_REQUIRED(_font_clear_size_cache, RID);
+ GDVIRTUAL2_REQUIRED(_font_remove_size_cache, RID, const Vector2i &);
virtual void font_set_ascent(const RID &p_font_rid, int64_t p_size, double p_ascent) override;
virtual double font_get_ascent(const RID &p_font_rid, int64_t p_size) const override;
- GDVIRTUAL3(_font_set_ascent, RID, int64_t, double);
- GDVIRTUAL2RC(double, _font_get_ascent, RID, int64_t);
+ GDVIRTUAL3_REQUIRED(_font_set_ascent, RID, int64_t, double);
+ GDVIRTUAL2RC_REQUIRED(double, _font_get_ascent, RID, int64_t);
virtual void font_set_descent(const RID &p_font_rid, int64_t p_size, double p_descent) override;
virtual double font_get_descent(const RID &p_font_rid, int64_t p_size) const override;
- GDVIRTUAL3(_font_set_descent, RID, int64_t, double);
- GDVIRTUAL2RC(double, _font_get_descent, RID, int64_t);
+ GDVIRTUAL3_REQUIRED(_font_set_descent, RID, int64_t, double);
+ GDVIRTUAL2RC_REQUIRED(double, _font_get_descent, RID, int64_t);
virtual void font_set_underline_position(const RID &p_font_rid, int64_t p_size, double p_underline_position) override;
virtual double font_get_underline_position(const RID &p_font_rid, int64_t p_size) const override;
- GDVIRTUAL3(_font_set_underline_position, RID, int64_t, double);
- GDVIRTUAL2RC(double, _font_get_underline_position, RID, int64_t);
+ GDVIRTUAL3_REQUIRED(_font_set_underline_position, RID, int64_t, double);
+ GDVIRTUAL2RC_REQUIRED(double, _font_get_underline_position, RID, int64_t);
virtual void font_set_underline_thickness(const RID &p_font_rid, int64_t p_size, double p_underline_thickness) override;
virtual double font_get_underline_thickness(const RID &p_font_rid, int64_t p_size) const override;
- GDVIRTUAL3(_font_set_underline_thickness, RID, int64_t, double);
- GDVIRTUAL2RC(double, _font_get_underline_thickness, RID, int64_t);
+ GDVIRTUAL3_REQUIRED(_font_set_underline_thickness, RID, int64_t, double);
+ GDVIRTUAL2RC_REQUIRED(double, _font_get_underline_thickness, RID, int64_t);
virtual void font_set_scale(const RID &p_font_rid, int64_t p_size, double p_scale) override;
virtual double font_get_scale(const RID &p_font_rid, int64_t p_size) const override;
- GDVIRTUAL3(_font_set_scale, RID, int64_t, double);
- GDVIRTUAL2RC(double, _font_get_scale, RID, int64_t);
+ GDVIRTUAL3_REQUIRED(_font_set_scale, RID, int64_t, double);
+ GDVIRTUAL2RC_REQUIRED(double, _font_get_scale, RID, int64_t);
virtual int64_t font_get_texture_count(const RID &p_font_rid, const Vector2i &p_size) const override;
virtual void font_clear_textures(const RID &p_font_rid, const Vector2i &p_size) override;
virtual void font_remove_texture(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) override;
- GDVIRTUAL2RC(int64_t, _font_get_texture_count, RID, const Vector2i &);
- GDVIRTUAL2(_font_clear_textures, RID, const Vector2i &);
- GDVIRTUAL3(_font_remove_texture, RID, const Vector2i &, int64_t);
+ GDVIRTUAL2RC_REQUIRED(int64_t, _font_get_texture_count, RID, const Vector2i &);
+ GDVIRTUAL2_REQUIRED(_font_clear_textures, RID, const Vector2i &);
+ GDVIRTUAL3_REQUIRED(_font_remove_texture, RID, const Vector2i &, int64_t);
virtual void font_set_texture_image(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index, const Ref<Image> &p_image) override;
virtual Ref<Image> font_get_texture_image(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) const override;
- GDVIRTUAL4(_font_set_texture_image, RID, const Vector2i &, int64_t, const Ref<Image> &);
- GDVIRTUAL3RC(Ref<Image>, _font_get_texture_image, RID, const Vector2i &, int64_t);
+ GDVIRTUAL4_REQUIRED(_font_set_texture_image, RID, const Vector2i &, int64_t, const Ref<Image> &);
+ GDVIRTUAL3RC_REQUIRED(Ref<Image>, _font_get_texture_image, RID, const Vector2i &, int64_t);
virtual void font_set_texture_offsets(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index, const PackedInt32Array &p_offset) override;
virtual PackedInt32Array font_get_texture_offsets(const RID &p_font_rid, const Vector2i &p_size, int64_t p_texture_index) const override;
@@ -265,40 +265,40 @@ public:
virtual PackedInt32Array font_get_glyph_list(const RID &p_font_rid, const Vector2i &p_size) const override;
virtual void font_clear_glyphs(const RID &p_font_rid, const Vector2i &p_size) override;
virtual void font_remove_glyph(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) override;
- GDVIRTUAL2RC(PackedInt32Array, _font_get_glyph_list, RID, const Vector2i &);
- GDVIRTUAL2(_font_clear_glyphs, RID, const Vector2i &);
- GDVIRTUAL3(_font_remove_glyph, RID, const Vector2i &, int64_t);
+ GDVIRTUAL2RC_REQUIRED(PackedInt32Array, _font_get_glyph_list, RID, const Vector2i &);
+ GDVIRTUAL2_REQUIRED(_font_clear_glyphs, RID, const Vector2i &);
+ GDVIRTUAL3_REQUIRED(_font_remove_glyph, RID, const Vector2i &, int64_t);
virtual Vector2 font_get_glyph_advance(const RID &p_font_rid, int64_t p_size, int64_t p_glyph) const override;
virtual void font_set_glyph_advance(const RID &p_font_rid, int64_t p_size, int64_t p_glyph, const Vector2 &p_advance) override;
- GDVIRTUAL3RC(Vector2, _font_get_glyph_advance, RID, int64_t, int64_t);
- GDVIRTUAL4(_font_set_glyph_advance, RID, int64_t, int64_t, const Vector2 &);
+ GDVIRTUAL3RC_REQUIRED(Vector2, _font_get_glyph_advance, RID, int64_t, int64_t);
+ GDVIRTUAL4_REQUIRED(_font_set_glyph_advance, RID, int64_t, int64_t, const Vector2 &);
virtual Vector2 font_get_glyph_offset(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const override;
virtual void font_set_glyph_offset(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Vector2 &p_offset) override;
- GDVIRTUAL3RC(Vector2, _font_get_glyph_offset, RID, const Vector2i &, int64_t);
- GDVIRTUAL4(_font_set_glyph_offset, RID, const Vector2i &, int64_t, const Vector2 &);
+ GDVIRTUAL3RC_REQUIRED(Vector2, _font_get_glyph_offset, RID, const Vector2i &, int64_t);
+ GDVIRTUAL4_REQUIRED(_font_set_glyph_offset, RID, const Vector2i &, int64_t, const Vector2 &);
virtual Vector2 font_get_glyph_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const override;
virtual void font_set_glyph_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Vector2 &p_gl_size) override;
- GDVIRTUAL3RC(Vector2, _font_get_glyph_size, RID, const Vector2i &, int64_t);
- GDVIRTUAL4(_font_set_glyph_size, RID, const Vector2i &, int64_t, const Vector2 &);
+ GDVIRTUAL3RC_REQUIRED(Vector2, _font_get_glyph_size, RID, const Vector2i &, int64_t);
+ GDVIRTUAL4_REQUIRED(_font_set_glyph_size, RID, const Vector2i &, int64_t, const Vector2 &);
virtual Rect2 font_get_glyph_uv_rect(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const override;
virtual void font_set_glyph_uv_rect(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, const Rect2 &p_uv_rect) override;
- GDVIRTUAL3RC(Rect2, _font_get_glyph_uv_rect, RID, const Vector2i &, int64_t);
- GDVIRTUAL4(_font_set_glyph_uv_rect, RID, const Vector2i &, int64_t, const Rect2 &);
+ GDVIRTUAL3RC_REQUIRED(Rect2, _font_get_glyph_uv_rect, RID, const Vector2i &, int64_t);
+ GDVIRTUAL4_REQUIRED(_font_set_glyph_uv_rect, RID, const Vector2i &, int64_t, const Rect2 &);
virtual int64_t font_get_glyph_texture_idx(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const override;
virtual void font_set_glyph_texture_idx(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph, int64_t p_texture_idx) override;
- GDVIRTUAL3RC(int64_t, _font_get_glyph_texture_idx, RID, const Vector2i &, int64_t);
- GDVIRTUAL4(_font_set_glyph_texture_idx, RID, const Vector2i &, int64_t, int64_t);
+ GDVIRTUAL3RC_REQUIRED(int64_t, _font_get_glyph_texture_idx, RID, const Vector2i &, int64_t);
+ GDVIRTUAL4_REQUIRED(_font_set_glyph_texture_idx, RID, const Vector2i &, int64_t, int64_t);
virtual RID font_get_glyph_texture_rid(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const override;
- GDVIRTUAL3RC(RID, _font_get_glyph_texture_rid, RID, const Vector2i &, int64_t);
+ GDVIRTUAL3RC_REQUIRED(RID, _font_get_glyph_texture_rid, RID, const Vector2i &, int64_t);
virtual Size2 font_get_glyph_texture_size(const RID &p_font_rid, const Vector2i &p_size, int64_t p_glyph) const override;
- GDVIRTUAL3RC(Size2, _font_get_glyph_texture_size, RID, const Vector2i &, int64_t);
+ GDVIRTUAL3RC_REQUIRED(Size2, _font_get_glyph_texture_size, RID, const Vector2i &, int64_t);
virtual Dictionary font_get_glyph_contours(const RID &p_font, int64_t p_size, int64_t p_index) const override;
GDVIRTUAL3RC(Dictionary, _font_get_glyph_contours, RID, int64_t, int64_t);
@@ -316,17 +316,17 @@ public:
GDVIRTUAL3RC(Vector2, _font_get_kerning, RID, int64_t, const Vector2i &);
virtual int64_t font_get_glyph_index(const RID &p_font_rid, int64_t p_size, int64_t p_char, int64_t p_variation_selector = 0) const override;
- GDVIRTUAL4RC(int64_t, _font_get_glyph_index, RID, int64_t, int64_t, int64_t);
+ GDVIRTUAL4RC_REQUIRED(int64_t, _font_get_glyph_index, RID, int64_t, int64_t, int64_t);
virtual int64_t font_get_char_from_glyph_index(const RID &p_font_rid, int64_t p_size, int64_t p_glyph_index) const override;
- GDVIRTUAL3RC(int64_t, _font_get_char_from_glyph_index, RID, int64_t, int64_t);
+ GDVIRTUAL3RC_REQUIRED(int64_t, _font_get_char_from_glyph_index, RID, int64_t, int64_t);
virtual bool font_has_char(const RID &p_font_rid, int64_t p_char) const override;
virtual String font_get_supported_chars(const RID &p_font_rid) const override;
virtual PackedInt32Array font_get_supported_glyphs(const RID &p_font_rid) const override;
- GDVIRTUAL2RC(bool, _font_has_char, RID, int64_t);
- GDVIRTUAL1RC(String, _font_get_supported_chars, RID);
- GDVIRTUAL1RC(PackedInt32Array, _font_get_supported_glyphs, RID);
+ GDVIRTUAL2RC_REQUIRED(bool, _font_has_char, RID, int64_t);
+ GDVIRTUAL1RC_REQUIRED(String, _font_get_supported_chars, RID);
+ GDVIRTUAL1RC_REQUIRED(PackedInt32Array, _font_get_supported_glyphs, RID);
virtual void font_render_range(const RID &p_font, const Vector2i &p_size, int64_t p_start, int64_t p_end) override;
virtual void font_render_glyph(const RID &p_font_rid, const Vector2i &p_size, int64_t p_index) override;
@@ -335,8 +335,8 @@ public:
virtual void font_draw_glyph(const RID &p_font, const RID &p_canvas, int64_t p_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color = Color(1, 1, 1)) const override;
virtual void font_draw_glyph_outline(const RID &p_font, const RID &p_canvas, int64_t p_size, int64_t p_outline_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color = Color(1, 1, 1)) const override;
- GDVIRTUAL6C(_font_draw_glyph, RID, RID, int64_t, const Vector2 &, int64_t, const Color &);
- GDVIRTUAL7C(_font_draw_glyph_outline, RID, RID, int64_t, int64_t, const Vector2 &, int64_t, const Color &);
+ GDVIRTUAL6C_REQUIRED(_font_draw_glyph, RID, RID, int64_t, const Vector2 &, int64_t, const Color &);
+ GDVIRTUAL7C_REQUIRED(_font_draw_glyph_outline, RID, RID, int64_t, int64_t, const Vector2 &, int64_t, const Color &);
virtual bool font_is_language_supported(const RID &p_font_rid, const String &p_language) const override;
virtual void font_set_language_support_override(const RID &p_font_rid, const String &p_language, bool p_supported) override;
@@ -383,10 +383,10 @@ public:
/* Shaped text buffer interface */
virtual RID create_shaped_text(Direction p_direction = DIRECTION_AUTO, Orientation p_orientation = ORIENTATION_HORIZONTAL) override;
- GDVIRTUAL2R(RID, _create_shaped_text, Direction, Orientation);
+ GDVIRTUAL2R_REQUIRED(RID, _create_shaped_text, Direction, Orientation);
virtual void shaped_text_clear(const RID &p_shaped) override;
- GDVIRTUAL1(_shaped_text_clear, RID);
+ GDVIRTUAL1_REQUIRED(_shaped_text_clear, RID);
virtual void shaped_text_set_direction(const RID &p_shaped, Direction p_direction = DIRECTION_AUTO) override;
virtual Direction shaped_text_get_direction(const RID &p_shaped) const override;
@@ -431,21 +431,21 @@ public:
virtual bool shaped_text_add_string(const RID &p_shaped, const String &p_text, const TypedArray<RID> &p_fonts, int64_t p_size, const Dictionary &p_opentype_features = Dictionary(), const String &p_language = "", const Variant &p_meta = Variant()) override;
virtual bool shaped_text_add_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, int64_t p_length = 1, double p_baseline = 0.0) override;
virtual bool shaped_text_resize_object(const RID &p_shaped, const Variant &p_key, const Size2 &p_size, InlineAlignment p_inline_align = INLINE_ALIGNMENT_CENTER, double p_baseline = 0.0) override;
- GDVIRTUAL7R(bool, _shaped_text_add_string, RID, const String &, const TypedArray<RID> &, int64_t, const Dictionary &, const String &, const Variant &);
- GDVIRTUAL6R(bool, _shaped_text_add_object, RID, const Variant &, const Size2 &, InlineAlignment, int64_t, double);
- GDVIRTUAL5R(bool, _shaped_text_resize_object, RID, const Variant &, const Size2 &, InlineAlignment, double);
+ GDVIRTUAL7R_REQUIRED(bool, _shaped_text_add_string, RID, const String &, const TypedArray<RID> &, int64_t, const Dictionary &, const String &, const Variant &);
+ GDVIRTUAL6R_REQUIRED(bool, _shaped_text_add_object, RID, const Variant &, const Size2 &, InlineAlignment, int64_t, double);
+ GDVIRTUAL5R_REQUIRED(bool, _shaped_text_resize_object, RID, const Variant &, const Size2 &, InlineAlignment, double);
virtual int64_t shaped_get_span_count(const RID &p_shaped) const override;
virtual Variant shaped_get_span_meta(const RID &p_shaped, int64_t p_index) const override;
virtual void shaped_set_span_update_font(const RID &p_shaped, int64_t p_index, const TypedArray<RID> &p_fonts, int64_t p_size, const Dictionary &p_opentype_features = Dictionary()) override;
- GDVIRTUAL1RC(int64_t, _shaped_get_span_count, RID);
- GDVIRTUAL2RC(Variant, _shaped_get_span_meta, RID, int64_t);
- GDVIRTUAL5(_shaped_set_span_update_font, RID, int64_t, const TypedArray<RID> &, int64_t, const Dictionary &);
+ GDVIRTUAL1RC_REQUIRED(int64_t, _shaped_get_span_count, RID);
+ GDVIRTUAL2RC_REQUIRED(Variant, _shaped_get_span_meta, RID, int64_t);
+ GDVIRTUAL5_REQUIRED(_shaped_set_span_update_font, RID, int64_t, const TypedArray<RID> &, int64_t, const Dictionary &);
virtual RID shaped_text_substr(const RID &p_shaped, int64_t p_start, int64_t p_length) const override;
virtual RID shaped_text_get_parent(const RID &p_shaped) const override;
- GDVIRTUAL3RC(RID, _shaped_text_substr, RID, int64_t, int64_t);
- GDVIRTUAL1RC(RID, _shaped_text_get_parent, RID);
+ GDVIRTUAL3RC_REQUIRED(RID, _shaped_text_substr, RID, int64_t, int64_t);
+ GDVIRTUAL1RC_REQUIRED(RID, _shaped_text_get_parent, RID);
virtual double shaped_text_fit_to_width(const RID &p_shaped, double p_width, BitField<TextServer::JustificationFlag> p_jst_flags = JUSTIFICATION_WORD_BOUND | JUSTIFICATION_KASHIDA) override;
virtual double shaped_text_tab_align(const RID &p_shaped, const PackedFloat32Array &p_tab_stops) override;
@@ -455,22 +455,22 @@ public:
virtual bool shaped_text_shape(const RID &p_shaped) override;
virtual bool shaped_text_update_breaks(const RID &p_shaped) override;
virtual bool shaped_text_update_justification_ops(const RID &p_shaped) override;
- GDVIRTUAL1R(bool, _shaped_text_shape, RID);
+ GDVIRTUAL1R_REQUIRED(bool, _shaped_text_shape, RID);
GDVIRTUAL1R(bool, _shaped_text_update_breaks, RID);
GDVIRTUAL1R(bool, _shaped_text_update_justification_ops, RID);
virtual bool shaped_text_is_ready(const RID &p_shaped) const override;
- GDVIRTUAL1RC(bool, _shaped_text_is_ready, RID);
+ GDVIRTUAL1RC_REQUIRED(bool, _shaped_text_is_ready, RID);
virtual const Glyph *shaped_text_get_glyphs(const RID &p_shaped) const override;
virtual const Glyph *shaped_text_sort_logical(const RID &p_shaped) override;
virtual int64_t shaped_text_get_glyph_count(const RID &p_shaped) const override;
- GDVIRTUAL1RC(GDExtensionConstPtr<const Glyph>, _shaped_text_get_glyphs, RID);
- GDVIRTUAL1R(GDExtensionConstPtr<const Glyph>, _shaped_text_sort_logical, RID);
- GDVIRTUAL1RC(int64_t, _shaped_text_get_glyph_count, RID);
+ GDVIRTUAL1RC_REQUIRED(GDExtensionConstPtr<const Glyph>, _shaped_text_get_glyphs, RID);
+ GDVIRTUAL1R_REQUIRED(GDExtensionConstPtr<const Glyph>, _shaped_text_sort_logical, RID);
+ GDVIRTUAL1RC_REQUIRED(int64_t, _shaped_text_get_glyph_count, RID);
virtual Vector2i shaped_text_get_range(const RID &p_shaped) const override;
- GDVIRTUAL1RC(Vector2i, _shaped_text_get_range, RID);
+ GDVIRTUAL1RC_REQUIRED(Vector2i, _shaped_text_get_range, RID);
virtual PackedInt32Array shaped_text_get_line_breaks_adv(const RID &p_shaped, const PackedFloat32Array &p_width, int64_t p_start = 0, bool p_once = true, BitField<TextServer::LineBreakFlag> p_break_flags = BREAK_MANDATORY | BREAK_WORD_BOUND) const override;
virtual PackedInt32Array shaped_text_get_line_breaks(const RID &p_shaped, double p_width, int64_t p_start = 0, BitField<TextServer::LineBreakFlag> p_break_flags = BREAK_MANDATORY | BREAK_WORD_BOUND) const override;
@@ -483,10 +483,10 @@ public:
virtual int64_t shaped_text_get_ellipsis_pos(const RID &p_shaped) const override;
virtual const Glyph *shaped_text_get_ellipsis_glyphs(const RID &p_shaped) const override;
virtual int64_t shaped_text_get_ellipsis_glyph_count(const RID &p_shaped) const override;
- GDVIRTUAL1RC(int64_t, _shaped_text_get_trim_pos, RID);
- GDVIRTUAL1RC(int64_t, _shaped_text_get_ellipsis_pos, RID);
- GDVIRTUAL1RC(GDExtensionConstPtr<const Glyph>, _shaped_text_get_ellipsis_glyphs, RID);
- GDVIRTUAL1RC(int64_t, _shaped_text_get_ellipsis_glyph_count, RID);
+ GDVIRTUAL1RC_REQUIRED(int64_t, _shaped_text_get_trim_pos, RID);
+ GDVIRTUAL1RC_REQUIRED(int64_t, _shaped_text_get_ellipsis_pos, RID);
+ GDVIRTUAL1RC_REQUIRED(GDExtensionConstPtr<const Glyph>, _shaped_text_get_ellipsis_glyphs, RID);
+ GDVIRTUAL1RC_REQUIRED(int64_t, _shaped_text_get_ellipsis_glyph_count, RID);
virtual void shaped_text_overrun_trim_to_width(const RID &p_shaped, double p_width, BitField<TextServer::TextOverrunFlag> p_trim_flags) override;
GDVIRTUAL3(_shaped_text_overrun_trim_to_width, RID, double, BitField<TextServer::TextOverrunFlag>);
@@ -495,10 +495,10 @@ public:
virtual Rect2 shaped_text_get_object_rect(const RID &p_shaped, const Variant &p_key) const override;
virtual Vector2i shaped_text_get_object_range(const RID &p_shaped, const Variant &p_key) const override;
virtual int64_t shaped_text_get_object_glyph(const RID &p_shaped, const Variant &p_key) const override;
- GDVIRTUAL1RC(Array, _shaped_text_get_objects, RID);
- GDVIRTUAL2RC(Rect2, _shaped_text_get_object_rect, RID, const Variant &);
- GDVIRTUAL2RC(Vector2i, _shaped_text_get_object_range, RID, const Variant &);
- GDVIRTUAL2RC(int64_t, _shaped_text_get_object_glyph, RID, const Variant &);
+ GDVIRTUAL1RC_REQUIRED(Array, _shaped_text_get_objects, RID);
+ GDVIRTUAL2RC_REQUIRED(Rect2, _shaped_text_get_object_rect, RID, const Variant &);
+ GDVIRTUAL2RC_REQUIRED(Vector2i, _shaped_text_get_object_range, RID, const Variant &);
+ GDVIRTUAL2RC_REQUIRED(int64_t, _shaped_text_get_object_glyph, RID, const Variant &);
virtual Size2 shaped_text_get_size(const RID &p_shaped) const override;
virtual double shaped_text_get_ascent(const RID &p_shaped) const override;
@@ -506,12 +506,12 @@ public:
virtual double shaped_text_get_width(const RID &p_shaped) const override;
virtual double shaped_text_get_underline_position(const RID &p_shaped) const override;
virtual double shaped_text_get_underline_thickness(const RID &p_shaped) const override;
- GDVIRTUAL1RC(Size2, _shaped_text_get_size, RID);
- GDVIRTUAL1RC(double, _shaped_text_get_ascent, RID);
- GDVIRTUAL1RC(double, _shaped_text_get_descent, RID);
- GDVIRTUAL1RC(double, _shaped_text_get_width, RID);
- GDVIRTUAL1RC(double, _shaped_text_get_underline_position, RID);
- GDVIRTUAL1RC(double, _shaped_text_get_underline_thickness, RID);
+ GDVIRTUAL1RC_REQUIRED(Size2, _shaped_text_get_size, RID);
+ GDVIRTUAL1RC_REQUIRED(double, _shaped_text_get_ascent, RID);
+ GDVIRTUAL1RC_REQUIRED(double, _shaped_text_get_descent, RID);
+ GDVIRTUAL1RC_REQUIRED(double, _shaped_text_get_width, RID);
+ GDVIRTUAL1RC_REQUIRED(double, _shaped_text_get_underline_position, RID);
+ GDVIRTUAL1RC_REQUIRED(double, _shaped_text_get_underline_thickness, RID);
virtual Direction shaped_text_get_dominant_direction_in_range(const RID &p_shaped, int64_t p_start, int64_t p_end) const override;
GDVIRTUAL3RC(int64_t, _shaped_text_get_dominant_direction_in_range, RID, int64_t, int64_t);
diff --git a/tests/core/string/test_string.h b/tests/core/string/test_string.h
index 301771a3de..0131f4c02a 100644
--- a/tests/core/string/test_string.h
+++ b/tests/core/string/test_string.h
@@ -1988,6 +1988,46 @@ TEST_CASE("[String] Variant ptr indexed set") {
CHECK_EQ(s, String("azcd"));
}
+TEST_CASE("[String] parse_url") {
+ String scheme, host, path, fragment;
+ int port;
+
+ SUBCASE("Typical URL") {
+ Error err = String("https://docs.godotengine.org/en/stable/").parse_url(scheme, host, port, path, fragment);
+ REQUIRE(err == OK);
+ CHECK_EQ(scheme, "https://");
+ CHECK_EQ(host, "docs.godotengine.org");
+ CHECK_EQ(port, 0);
+ CHECK_EQ(path, "/en/stable/");
+ CHECK_EQ(fragment, "");
+ }
+
+ SUBCASE("All Elements") {
+ Error err = String("https://www.example.com:8080/path/to/file.html#fragment").parse_url(scheme, host, port, path, fragment);
+ REQUIRE(err == OK);
+ CHECK_EQ(scheme, "https://");
+ CHECK_EQ(host, "www.example.com");
+ CHECK_EQ(port, 8080);
+ CHECK_EQ(path, "/path/to/file.html");
+ CHECK_EQ(fragment, "fragment");
+ }
+
+ SUBCASE("Invalid Scheme") {
+ Error err = String("http_://example.com").parse_url(scheme, host, port, path, fragment);
+ REQUIRE(err == ERR_INVALID_PARAMETER); // Host being empty is an error.
+ }
+
+ SUBCASE("Scheme vs Fragment") {
+ Error err = String("google.com/#goto=http://redirect_url/").parse_url(scheme, host, port, path, fragment);
+ REQUIRE(err == OK);
+ CHECK_EQ(scheme, "");
+ CHECK_EQ(host, "google.com");
+ CHECK_EQ(port, 0);
+ CHECK_EQ(path, "/");
+ CHECK_EQ(fragment, "goto=http://redirect_url/");
+ }
+}
+
TEST_CASE("[Stress][String] Empty via ' == String()'") {
for (int i = 0; i < 100000; ++i) {
String str = "Hello World!";
diff --git a/thirdparty/README.md b/thirdparty/README.md
index 4c47b91c8c..a219839afc 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -650,6 +650,10 @@ comments and a patch is provided in the `patches` folder.
Collection of single-file libraries used in Godot components.
+- `bcdec.h`
+ * Upstream: https://github.com/iOrange/bcdec
+ * Version: git (026acf98ea271045cb10713daa96ba98528badb7, 2022)
+ * License: MIT
- `clipper.{cpp,hpp}`
* Upstream: https://sourceforge.net/projects/polyclipping
* Version: 6.4.2 (2017) + Godot changes (added optional exceptions handling)
@@ -757,7 +761,7 @@ with the provided patch.
## openxr
- Upstream: https://github.com/KhronosGroup/OpenXR-SDK
-- Version: 1.1.38 (f90488c4fb1537f4256d09d4a4d3ad5543ebaf24, 2024)
+- Version: 1.1.41 (7d1c0961351bac61fd7bb72d402649d5ac3f2935, 2024)
- License: Apache 2.0
Files extracted from upstream source:
@@ -873,23 +877,6 @@ They can be reapplied using the patches included in the `patches`
folder, in order.
-## squish
-
-- Upstream: https://sourceforge.net/projects/libsquish
-- Version: 1.15 (r104, 2017)
-- License: MIT
-
-Files extracted from upstream source:
-
-- `LICENSE.txt`
-- All `.cpp`, `.h` and `.inl` files
-
-Some downstream changes have been made and are identified by
-`// -- GODOT begin --` and `// -- GODOT end --` comments.
-They can be reapplied using the patches included in the `patches`
-folder.
-
-
## tinyexr
- Upstream: https://github.com/syoyo/tinyexr
diff --git a/thirdparty/mbedtls/include/godot_module_mbedtls_config.h b/thirdparty/mbedtls/include/godot_module_mbedtls_config.h
index 2011827b7a..58bf1d9d54 100644
--- a/thirdparty/mbedtls/include/godot_module_mbedtls_config.h
+++ b/thirdparty/mbedtls/include/godot_module_mbedtls_config.h
@@ -59,18 +59,6 @@
// Disable deprecated
#define MBEDTLS_DEPRECATED_REMOVED
-// mbedTLS 3.6 finally enabled TLSv1.3 by default, but it requires some mobule
-// changes, and to enable PSA crypto (new "standard" API specification).
-// Disable it for now.
-#undef MBEDTLS_SSL_PROTO_TLS1_3
-
-// Disable PSA Crypto.
-#undef MBEDTLS_PSA_CRYPTO_CONFIG
-#undef MBEDTLS_PSA_CRYPTO_C
-#undef MBEDTLS_PSA_CRYPTO_STORAGE_C
-#undef MBEDTLS_PSA_ITS_FILE_C
-#undef MBEDTLS_LMS_C
-
#endif // GODOT_MBEDTLS_INCLUDE_H
#endif // GODOT_MODULE_MBEDTLS_CONFIG_H
diff --git a/thirdparty/mbedtls/library/psa_crypto.c b/thirdparty/mbedtls/library/psa_crypto.c
new file mode 100644
index 0000000000..c4f41db10b
--- /dev/null
+++ b/thirdparty/mbedtls/library/psa_crypto.c
@@ -0,0 +1,9233 @@
+/*
+ * PSA crypto layer on top of Mbed TLS crypto
+ */
+/*
+ * Copyright The Mbed TLS Contributors
+ * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#include "common.h"
+#include "psa_crypto_core_common.h"
+
+#if defined(MBEDTLS_PSA_CRYPTO_C)
+
+#if defined(MBEDTLS_PSA_CRYPTO_CONFIG)
+#include "check_crypto_config.h"
+#endif
+
+#include "psa/crypto.h"
+#include "psa/crypto_values.h"
+
+#include "psa_crypto_cipher.h"
+#include "psa_crypto_core.h"
+#include "psa_crypto_invasive.h"
+#include "psa_crypto_driver_wrappers.h"
+#include "psa_crypto_driver_wrappers_no_static.h"
+#include "psa_crypto_ecp.h"
+#include "psa_crypto_ffdh.h"
+#include "psa_crypto_hash.h"
+#include "psa_crypto_mac.h"
+#include "psa_crypto_rsa.h"
+#include "psa_crypto_ecp.h"
+#if defined(MBEDTLS_PSA_CRYPTO_SE_C)
+#include "psa_crypto_se.h"
+#endif
+#include "psa_crypto_slot_management.h"
+/* Include internal declarations that are useful for implementing persistently
+ * stored keys. */
+#include "psa_crypto_storage.h"
+
+#include "psa_crypto_random_impl.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include "mbedtls/platform.h"
+
+#include "mbedtls/aes.h"
+#include "mbedtls/asn1.h"
+#include "mbedtls/asn1write.h"
+#include "mbedtls/bignum.h"
+#include "mbedtls/camellia.h"
+#include "mbedtls/chacha20.h"
+#include "mbedtls/chachapoly.h"
+#include "mbedtls/cipher.h"
+#include "mbedtls/ccm.h"
+#include "mbedtls/cmac.h"
+#include "mbedtls/constant_time.h"
+#include "mbedtls/des.h"
+#include "mbedtls/ecdh.h"
+#include "mbedtls/ecp.h"
+#include "mbedtls/entropy.h"
+#include "mbedtls/error.h"
+#include "mbedtls/gcm.h"
+#include "mbedtls/md5.h"
+#include "mbedtls/pk.h"
+#include "pk_wrap.h"
+#include "mbedtls/platform_util.h"
+#include "mbedtls/error.h"
+#include "mbedtls/ripemd160.h"
+#include "mbedtls/rsa.h"
+#include "mbedtls/sha1.h"
+#include "mbedtls/sha256.h"
+#include "mbedtls/sha512.h"
+#include "mbedtls/psa_util.h"
+#include "mbedtls/threading.h"
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF) || \
+ defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT) || \
+ defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXPAND)
+#define BUILTIN_ALG_ANY_HKDF 1
+#endif
+
+/****************************************************************/
+/* Global data, support functions and library management */
+/****************************************************************/
+
+static int key_type_is_raw_bytes(psa_key_type_t type)
+{
+ return PSA_KEY_TYPE_IS_UNSTRUCTURED(type);
+}
+
+/* Values for psa_global_data_t::rng_state */
+#define RNG_NOT_INITIALIZED 0
+#define RNG_INITIALIZED 1
+#define RNG_SEEDED 2
+
+/* IDs for PSA crypto subsystems. Starts at 1 to catch potential uninitialized
+ * variables as arguments. */
+typedef enum {
+ PSA_CRYPTO_SUBSYSTEM_DRIVER_WRAPPERS = 1,
+ PSA_CRYPTO_SUBSYSTEM_KEY_SLOTS,
+ PSA_CRYPTO_SUBSYSTEM_RNG,
+ PSA_CRYPTO_SUBSYSTEM_TRANSACTION,
+} mbedtls_psa_crypto_subsystem;
+
+/* Initialization flags for global_data::initialized */
+#define PSA_CRYPTO_SUBSYSTEM_DRIVER_WRAPPERS_INITIALIZED 0x01
+#define PSA_CRYPTO_SUBSYSTEM_KEY_SLOTS_INITIALIZED 0x02
+#define PSA_CRYPTO_SUBSYSTEM_TRANSACTION_INITIALIZED 0x04
+
+#define PSA_CRYPTO_SUBSYSTEM_ALL_INITIALISED ( \
+ PSA_CRYPTO_SUBSYSTEM_DRIVER_WRAPPERS_INITIALIZED | \
+ PSA_CRYPTO_SUBSYSTEM_KEY_SLOTS_INITIALIZED | \
+ PSA_CRYPTO_SUBSYSTEM_TRANSACTION_INITIALIZED)
+
+typedef struct {
+ uint8_t initialized;
+ uint8_t rng_state;
+ mbedtls_psa_random_context_t rng;
+} psa_global_data_t;
+
+static psa_global_data_t global_data;
+
+static uint8_t psa_get_initialized(void)
+{
+ uint8_t initialized;
+
+#if defined(MBEDTLS_THREADING_C)
+ mbedtls_mutex_lock(&mbedtls_threading_psa_rngdata_mutex);
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+ initialized = global_data.rng_state == RNG_SEEDED;
+
+#if defined(MBEDTLS_THREADING_C)
+ mbedtls_mutex_unlock(&mbedtls_threading_psa_rngdata_mutex);
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+#if defined(MBEDTLS_THREADING_C)
+ mbedtls_mutex_lock(&mbedtls_threading_psa_globaldata_mutex);
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+ initialized =
+ (initialized && (global_data.initialized == PSA_CRYPTO_SUBSYSTEM_ALL_INITIALISED));
+
+#if defined(MBEDTLS_THREADING_C)
+ mbedtls_mutex_unlock(&mbedtls_threading_psa_globaldata_mutex);
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+ return initialized;
+}
+
+static uint8_t psa_get_drivers_initialized(void)
+{
+ uint8_t initialized;
+
+#if defined(MBEDTLS_THREADING_C)
+ mbedtls_mutex_lock(&mbedtls_threading_psa_globaldata_mutex);
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+ initialized = (global_data.initialized & PSA_CRYPTO_SUBSYSTEM_DRIVER_WRAPPERS_INITIALIZED) != 0;
+
+#if defined(MBEDTLS_THREADING_C)
+ mbedtls_mutex_unlock(&mbedtls_threading_psa_globaldata_mutex);
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+ return initialized;
+}
+
+#define GUARD_MODULE_INITIALIZED \
+ if (psa_get_initialized() == 0) \
+ return PSA_ERROR_BAD_STATE;
+
+#if !defined(MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS)
+
+/* Declare a local copy of an input buffer and a variable that will be used
+ * to store a pointer to the start of the buffer.
+ *
+ * Note: This macro must be called before any operations which may jump to
+ * the exit label, so that the local input copy object is safe to be freed.
+ *
+ * Assumptions:
+ * - input is the name of a pointer to the buffer to be copied
+ * - The name LOCAL_INPUT_COPY_OF_input is unused in the current scope
+ * - input_copy_name is a name that is unused in the current scope
+ */
+#define LOCAL_INPUT_DECLARE(input, input_copy_name) \
+ psa_crypto_local_input_t LOCAL_INPUT_COPY_OF_##input = PSA_CRYPTO_LOCAL_INPUT_INIT; \
+ const uint8_t *input_copy_name = NULL;
+
+/* Allocate a copy of the buffer input and set the pointer input_copy to
+ * point to the start of the copy.
+ *
+ * Assumptions:
+ * - psa_status_t status exists
+ * - An exit label is declared
+ * - input is the name of a pointer to the buffer to be copied
+ * - LOCAL_INPUT_DECLARE(input, input_copy) has previously been called
+ */
+#define LOCAL_INPUT_ALLOC(input, length, input_copy) \
+ status = psa_crypto_local_input_alloc(input, length, \
+ &LOCAL_INPUT_COPY_OF_##input); \
+ if (status != PSA_SUCCESS) { \
+ goto exit; \
+ } \
+ input_copy = LOCAL_INPUT_COPY_OF_##input.buffer;
+
+/* Free the local input copy allocated previously by LOCAL_INPUT_ALLOC()
+ *
+ * Assumptions:
+ * - input_copy is the name of the input copy pointer set by LOCAL_INPUT_ALLOC()
+ * - input is the name of the original buffer that was copied
+ */
+#define LOCAL_INPUT_FREE(input, input_copy) \
+ input_copy = NULL; \
+ psa_crypto_local_input_free(&LOCAL_INPUT_COPY_OF_##input);
+
+/* Declare a local copy of an output buffer and a variable that will be used
+ * to store a pointer to the start of the buffer.
+ *
+ * Note: This macro must be called before any operations which may jump to
+ * the exit label, so that the local output copy object is safe to be freed.
+ *
+ * Assumptions:
+ * - output is the name of a pointer to the buffer to be copied
+ * - The name LOCAL_OUTPUT_COPY_OF_output is unused in the current scope
+ * - output_copy_name is a name that is unused in the current scope
+ */
+#define LOCAL_OUTPUT_DECLARE(output, output_copy_name) \
+ psa_crypto_local_output_t LOCAL_OUTPUT_COPY_OF_##output = PSA_CRYPTO_LOCAL_OUTPUT_INIT; \
+ uint8_t *output_copy_name = NULL;
+
+/* Allocate a copy of the buffer output and set the pointer output_copy to
+ * point to the start of the copy.
+ *
+ * Assumptions:
+ * - psa_status_t status exists
+ * - An exit label is declared
+ * - output is the name of a pointer to the buffer to be copied
+ * - LOCAL_OUTPUT_DECLARE(output, output_copy) has previously been called
+ */
+#define LOCAL_OUTPUT_ALLOC(output, length, output_copy) \
+ status = psa_crypto_local_output_alloc(output, length, \
+ &LOCAL_OUTPUT_COPY_OF_##output); \
+ if (status != PSA_SUCCESS) { \
+ goto exit; \
+ } \
+ output_copy = LOCAL_OUTPUT_COPY_OF_##output.buffer;
+
+/* Free the local output copy allocated previously by LOCAL_OUTPUT_ALLOC()
+ * after first copying back its contents to the original buffer.
+ *
+ * Assumptions:
+ * - psa_status_t status exists
+ * - output_copy is the name of the output copy pointer set by LOCAL_OUTPUT_ALLOC()
+ * - output is the name of the original buffer that was copied
+ */
+#define LOCAL_OUTPUT_FREE(output, output_copy) \
+ output_copy = NULL; \
+ do { \
+ psa_status_t local_output_status; \
+ local_output_status = psa_crypto_local_output_free(&LOCAL_OUTPUT_COPY_OF_##output); \
+ if (local_output_status != PSA_SUCCESS) { \
+ /* Since this error case is an internal error, it's more serious than \
+ * any existing error code and so it's fine to overwrite the existing \
+ * status. */ \
+ status = local_output_status; \
+ } \
+ } while (0)
+#else /* !MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS */
+#define LOCAL_INPUT_DECLARE(input, input_copy_name) \
+ const uint8_t *input_copy_name = NULL;
+#define LOCAL_INPUT_ALLOC(input, length, input_copy) \
+ input_copy = input;
+#define LOCAL_INPUT_FREE(input, input_copy) \
+ input_copy = NULL;
+#define LOCAL_OUTPUT_DECLARE(output, output_copy_name) \
+ uint8_t *output_copy_name = NULL;
+#define LOCAL_OUTPUT_ALLOC(output, length, output_copy) \
+ output_copy = output;
+#define LOCAL_OUTPUT_FREE(output, output_copy) \
+ output_copy = NULL;
+#endif /* !MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS */
+
+
+int psa_can_do_hash(psa_algorithm_t hash_alg)
+{
+ (void) hash_alg;
+ return psa_get_drivers_initialized();
+}
+
+int psa_can_do_cipher(psa_key_type_t key_type, psa_algorithm_t cipher_alg)
+{
+ (void) key_type;
+ (void) cipher_alg;
+ return psa_get_drivers_initialized();
+}
+
+
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_KEY_PAIR_IMPORT) || \
+ defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_PUBLIC_KEY) || \
+ defined(PSA_WANT_KEY_TYPE_DH_KEY_PAIR_GENERATE)
+static int psa_is_dh_key_size_valid(size_t bits)
+{
+ switch (bits) {
+#if defined(PSA_WANT_DH_RFC7919_2048)
+ case 2048:
+ return 1;
+#endif /* PSA_WANT_DH_RFC7919_2048 */
+#if defined(PSA_WANT_DH_RFC7919_3072)
+ case 3072:
+ return 1;
+#endif /* PSA_WANT_DH_RFC7919_3072 */
+#if defined(PSA_WANT_DH_RFC7919_4096)
+ case 4096:
+ return 1;
+#endif /* PSA_WANT_DH_RFC7919_4096 */
+#if defined(PSA_WANT_DH_RFC7919_6144)
+ case 6144:
+ return 1;
+#endif /* PSA_WANT_DH_RFC7919_6144 */
+#if defined(PSA_WANT_DH_RFC7919_8192)
+ case 8192:
+ return 1;
+#endif /* PSA_WANT_DH_RFC7919_8192 */
+ default:
+ return 0;
+ }
+}
+#endif /* MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_KEY_PAIR_IMPORT ||
+ MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_PUBLIC_KEY ||
+ PSA_WANT_KEY_TYPE_DH_KEY_PAIR_GENERATE */
+
+psa_status_t mbedtls_to_psa_error(int ret)
+{
+ /* Mbed TLS error codes can combine a high-level error code and a
+ * low-level error code. The low-level error usually reflects the
+ * root cause better, so dispatch on that preferably. */
+ int low_level_ret = -(-ret & 0x007f);
+ switch (low_level_ret != 0 ? low_level_ret : ret) {
+ case 0:
+ return PSA_SUCCESS;
+
+#if defined(MBEDTLS_AES_C)
+ case MBEDTLS_ERR_AES_INVALID_KEY_LENGTH:
+ case MBEDTLS_ERR_AES_INVALID_INPUT_LENGTH:
+ return PSA_ERROR_NOT_SUPPORTED;
+ case MBEDTLS_ERR_AES_BAD_INPUT_DATA:
+ return PSA_ERROR_INVALID_ARGUMENT;
+#endif
+
+#if defined(MBEDTLS_ASN1_PARSE_C) || defined(MBEDTLS_ASN1_WRITE_C)
+ case MBEDTLS_ERR_ASN1_OUT_OF_DATA:
+ case MBEDTLS_ERR_ASN1_UNEXPECTED_TAG:
+ case MBEDTLS_ERR_ASN1_INVALID_LENGTH:
+ case MBEDTLS_ERR_ASN1_LENGTH_MISMATCH:
+ case MBEDTLS_ERR_ASN1_INVALID_DATA:
+ return PSA_ERROR_INVALID_ARGUMENT;
+ case MBEDTLS_ERR_ASN1_ALLOC_FAILED:
+ return PSA_ERROR_INSUFFICIENT_MEMORY;
+ case MBEDTLS_ERR_ASN1_BUF_TOO_SMALL:
+ return PSA_ERROR_BUFFER_TOO_SMALL;
+#endif
+
+#if defined(MBEDTLS_CAMELLIA_C)
+ case MBEDTLS_ERR_CAMELLIA_BAD_INPUT_DATA:
+ case MBEDTLS_ERR_CAMELLIA_INVALID_INPUT_LENGTH:
+ return PSA_ERROR_NOT_SUPPORTED;
+#endif
+
+#if defined(MBEDTLS_CCM_C)
+ case MBEDTLS_ERR_CCM_BAD_INPUT:
+ return PSA_ERROR_INVALID_ARGUMENT;
+ case MBEDTLS_ERR_CCM_AUTH_FAILED:
+ return PSA_ERROR_INVALID_SIGNATURE;
+#endif
+
+#if defined(MBEDTLS_CHACHA20_C)
+ case MBEDTLS_ERR_CHACHA20_BAD_INPUT_DATA:
+ return PSA_ERROR_INVALID_ARGUMENT;
+#endif
+
+#if defined(MBEDTLS_CHACHAPOLY_C)
+ case MBEDTLS_ERR_CHACHAPOLY_BAD_STATE:
+ return PSA_ERROR_BAD_STATE;
+ case MBEDTLS_ERR_CHACHAPOLY_AUTH_FAILED:
+ return PSA_ERROR_INVALID_SIGNATURE;
+#endif
+
+#if defined(MBEDTLS_CIPHER_C)
+ case MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE:
+ return PSA_ERROR_NOT_SUPPORTED;
+ case MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA:
+ return PSA_ERROR_INVALID_ARGUMENT;
+ case MBEDTLS_ERR_CIPHER_ALLOC_FAILED:
+ return PSA_ERROR_INSUFFICIENT_MEMORY;
+ case MBEDTLS_ERR_CIPHER_INVALID_PADDING:
+ return PSA_ERROR_INVALID_PADDING;
+ case MBEDTLS_ERR_CIPHER_FULL_BLOCK_EXPECTED:
+ return PSA_ERROR_INVALID_ARGUMENT;
+ case MBEDTLS_ERR_CIPHER_AUTH_FAILED:
+ return PSA_ERROR_INVALID_SIGNATURE;
+ case MBEDTLS_ERR_CIPHER_INVALID_CONTEXT:
+ return PSA_ERROR_CORRUPTION_DETECTED;
+#endif
+
+#if !(defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG) || \
+ defined(MBEDTLS_PSA_HMAC_DRBG_MD_TYPE))
+ /* Only check CTR_DRBG error codes if underlying mbedtls_xxx
+ * functions are passed a CTR_DRBG instance. */
+ case MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED:
+ return PSA_ERROR_INSUFFICIENT_ENTROPY;
+ case MBEDTLS_ERR_CTR_DRBG_REQUEST_TOO_BIG:
+ case MBEDTLS_ERR_CTR_DRBG_INPUT_TOO_BIG:
+ return PSA_ERROR_NOT_SUPPORTED;
+ case MBEDTLS_ERR_CTR_DRBG_FILE_IO_ERROR:
+ return PSA_ERROR_INSUFFICIENT_ENTROPY;
+#endif
+
+#if defined(MBEDTLS_DES_C)
+ case MBEDTLS_ERR_DES_INVALID_INPUT_LENGTH:
+ return PSA_ERROR_NOT_SUPPORTED;
+#endif
+
+ case MBEDTLS_ERR_ENTROPY_NO_SOURCES_DEFINED:
+ case MBEDTLS_ERR_ENTROPY_NO_STRONG_SOURCE:
+ case MBEDTLS_ERR_ENTROPY_SOURCE_FAILED:
+ return PSA_ERROR_INSUFFICIENT_ENTROPY;
+
+#if defined(MBEDTLS_GCM_C)
+ case MBEDTLS_ERR_GCM_AUTH_FAILED:
+ return PSA_ERROR_INVALID_SIGNATURE;
+ case MBEDTLS_ERR_GCM_BUFFER_TOO_SMALL:
+ return PSA_ERROR_BUFFER_TOO_SMALL;
+ case MBEDTLS_ERR_GCM_BAD_INPUT:
+ return PSA_ERROR_INVALID_ARGUMENT;
+#endif
+
+#if !defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG) && \
+ defined(MBEDTLS_PSA_HMAC_DRBG_MD_TYPE)
+ /* Only check HMAC_DRBG error codes if underlying mbedtls_xxx
+ * functions are passed a HMAC_DRBG instance. */
+ case MBEDTLS_ERR_HMAC_DRBG_ENTROPY_SOURCE_FAILED:
+ return PSA_ERROR_INSUFFICIENT_ENTROPY;
+ case MBEDTLS_ERR_HMAC_DRBG_REQUEST_TOO_BIG:
+ case MBEDTLS_ERR_HMAC_DRBG_INPUT_TOO_BIG:
+ return PSA_ERROR_NOT_SUPPORTED;
+ case MBEDTLS_ERR_HMAC_DRBG_FILE_IO_ERROR:
+ return PSA_ERROR_INSUFFICIENT_ENTROPY;
+#endif
+
+#if defined(MBEDTLS_MD_LIGHT)
+ case MBEDTLS_ERR_MD_FEATURE_UNAVAILABLE:
+ return PSA_ERROR_NOT_SUPPORTED;
+ case MBEDTLS_ERR_MD_BAD_INPUT_DATA:
+ return PSA_ERROR_INVALID_ARGUMENT;
+ case MBEDTLS_ERR_MD_ALLOC_FAILED:
+ return PSA_ERROR_INSUFFICIENT_MEMORY;
+#if defined(MBEDTLS_FS_IO)
+ case MBEDTLS_ERR_MD_FILE_IO_ERROR:
+ return PSA_ERROR_STORAGE_FAILURE;
+#endif
+#endif
+
+#if defined(MBEDTLS_BIGNUM_C)
+#if defined(MBEDTLS_FS_IO)
+ case MBEDTLS_ERR_MPI_FILE_IO_ERROR:
+ return PSA_ERROR_STORAGE_FAILURE;
+#endif
+ case MBEDTLS_ERR_MPI_BAD_INPUT_DATA:
+ return PSA_ERROR_INVALID_ARGUMENT;
+ case MBEDTLS_ERR_MPI_INVALID_CHARACTER:
+ return PSA_ERROR_INVALID_ARGUMENT;
+ case MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL:
+ return PSA_ERROR_BUFFER_TOO_SMALL;
+ case MBEDTLS_ERR_MPI_NEGATIVE_VALUE:
+ return PSA_ERROR_INVALID_ARGUMENT;
+ case MBEDTLS_ERR_MPI_DIVISION_BY_ZERO:
+ return PSA_ERROR_INVALID_ARGUMENT;
+ case MBEDTLS_ERR_MPI_NOT_ACCEPTABLE:
+ return PSA_ERROR_INVALID_ARGUMENT;
+ case MBEDTLS_ERR_MPI_ALLOC_FAILED:
+ return PSA_ERROR_INSUFFICIENT_MEMORY;
+#endif
+
+#if defined(MBEDTLS_PK_C)
+ case MBEDTLS_ERR_PK_ALLOC_FAILED:
+ return PSA_ERROR_INSUFFICIENT_MEMORY;
+ case MBEDTLS_ERR_PK_TYPE_MISMATCH:
+ case MBEDTLS_ERR_PK_BAD_INPUT_DATA:
+ return PSA_ERROR_INVALID_ARGUMENT;
+#if defined(MBEDTLS_PSA_CRYPTO_STORAGE_C) || defined(MBEDTLS_FS_IO) || \
+ defined(MBEDTLS_PSA_ITS_FILE_C)
+ case MBEDTLS_ERR_PK_FILE_IO_ERROR:
+ return PSA_ERROR_STORAGE_FAILURE;
+#endif
+ case MBEDTLS_ERR_PK_KEY_INVALID_VERSION:
+ case MBEDTLS_ERR_PK_KEY_INVALID_FORMAT:
+ return PSA_ERROR_INVALID_ARGUMENT;
+ case MBEDTLS_ERR_PK_UNKNOWN_PK_ALG:
+ return PSA_ERROR_NOT_SUPPORTED;
+ case MBEDTLS_ERR_PK_PASSWORD_REQUIRED:
+ case MBEDTLS_ERR_PK_PASSWORD_MISMATCH:
+ return PSA_ERROR_NOT_PERMITTED;
+ case MBEDTLS_ERR_PK_INVALID_PUBKEY:
+ return PSA_ERROR_INVALID_ARGUMENT;
+ case MBEDTLS_ERR_PK_INVALID_ALG:
+ case MBEDTLS_ERR_PK_UNKNOWN_NAMED_CURVE:
+ case MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE:
+ return PSA_ERROR_NOT_SUPPORTED;
+ case MBEDTLS_ERR_PK_SIG_LEN_MISMATCH:
+ return PSA_ERROR_INVALID_SIGNATURE;
+ case MBEDTLS_ERR_PK_BUFFER_TOO_SMALL:
+ return PSA_ERROR_BUFFER_TOO_SMALL;
+#endif
+
+ case MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED:
+ return PSA_ERROR_HARDWARE_FAILURE;
+ case MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED:
+ return PSA_ERROR_NOT_SUPPORTED;
+
+#if defined(MBEDTLS_RSA_C)
+ case MBEDTLS_ERR_RSA_BAD_INPUT_DATA:
+ return PSA_ERROR_INVALID_ARGUMENT;
+ case MBEDTLS_ERR_RSA_INVALID_PADDING:
+ return PSA_ERROR_INVALID_PADDING;
+ case MBEDTLS_ERR_RSA_KEY_GEN_FAILED:
+ return PSA_ERROR_HARDWARE_FAILURE;
+ case MBEDTLS_ERR_RSA_KEY_CHECK_FAILED:
+ return PSA_ERROR_INVALID_ARGUMENT;
+ case MBEDTLS_ERR_RSA_PUBLIC_FAILED:
+ case MBEDTLS_ERR_RSA_PRIVATE_FAILED:
+ return PSA_ERROR_CORRUPTION_DETECTED;
+ case MBEDTLS_ERR_RSA_VERIFY_FAILED:
+ return PSA_ERROR_INVALID_SIGNATURE;
+ case MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE:
+ return PSA_ERROR_BUFFER_TOO_SMALL;
+ case MBEDTLS_ERR_RSA_RNG_FAILED:
+ return PSA_ERROR_INSUFFICIENT_ENTROPY;
+#endif
+
+#if defined(MBEDTLS_ECP_LIGHT)
+ case MBEDTLS_ERR_ECP_BAD_INPUT_DATA:
+ case MBEDTLS_ERR_ECP_INVALID_KEY:
+ return PSA_ERROR_INVALID_ARGUMENT;
+ case MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL:
+ return PSA_ERROR_BUFFER_TOO_SMALL;
+ case MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE:
+ return PSA_ERROR_NOT_SUPPORTED;
+ case MBEDTLS_ERR_ECP_SIG_LEN_MISMATCH:
+ case MBEDTLS_ERR_ECP_VERIFY_FAILED:
+ return PSA_ERROR_INVALID_SIGNATURE;
+ case MBEDTLS_ERR_ECP_ALLOC_FAILED:
+ return PSA_ERROR_INSUFFICIENT_MEMORY;
+ case MBEDTLS_ERR_ECP_RANDOM_FAILED:
+ return PSA_ERROR_INSUFFICIENT_ENTROPY;
+
+#if defined(MBEDTLS_ECP_RESTARTABLE)
+ case MBEDTLS_ERR_ECP_IN_PROGRESS:
+ return PSA_OPERATION_INCOMPLETE;
+#endif
+#endif
+
+ case MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED:
+ return PSA_ERROR_CORRUPTION_DETECTED;
+
+ default:
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+}
+
+/**
+ * \brief For output buffers which contain "tags"
+ * (outputs that may be checked for validity like
+ * hashes, MACs and signatures), fill the unused
+ * part of the output buffer (the whole buffer on
+ * error, the trailing part on success) with
+ * something that isn't a valid tag (barring an
+ * attack on the tag and deliberately-crafted
+ * input), in case the caller doesn't check the
+ * return status properly.
+ *
+ * \param output_buffer Pointer to buffer to wipe. May not be NULL
+ * unless \p output_buffer_size is zero.
+ * \param status Status of function called to generate
+ * output_buffer originally
+ * \param output_buffer_size Size of output buffer. If zero, \p output_buffer
+ * could be NULL.
+ * \param output_buffer_length Length of data written to output_buffer, must be
+ * less than \p output_buffer_size
+ */
+static void psa_wipe_tag_output_buffer(uint8_t *output_buffer, psa_status_t status,
+ size_t output_buffer_size, size_t output_buffer_length)
+{
+ size_t offset = 0;
+
+ if (output_buffer_size == 0) {
+ /* If output_buffer_size is 0 then we have nothing to do. We must not
+ call memset because output_buffer may be NULL in this case */
+ return;
+ }
+
+ if (status == PSA_SUCCESS) {
+ offset = output_buffer_length;
+ }
+
+ memset(output_buffer + offset, '!', output_buffer_size - offset);
+}
+
+
+psa_status_t psa_validate_unstructured_key_bit_size(psa_key_type_t type,
+ size_t bits)
+{
+ /* Check that the bit size is acceptable for the key type */
+ switch (type) {
+ case PSA_KEY_TYPE_RAW_DATA:
+ case PSA_KEY_TYPE_HMAC:
+ case PSA_KEY_TYPE_DERIVE:
+ case PSA_KEY_TYPE_PASSWORD:
+ case PSA_KEY_TYPE_PASSWORD_HASH:
+ break;
+#if defined(PSA_WANT_KEY_TYPE_AES)
+ case PSA_KEY_TYPE_AES:
+ if (bits != 128 && bits != 192 && bits != 256) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+ break;
+#endif
+#if defined(PSA_WANT_KEY_TYPE_ARIA)
+ case PSA_KEY_TYPE_ARIA:
+ if (bits != 128 && bits != 192 && bits != 256) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+ break;
+#endif
+#if defined(PSA_WANT_KEY_TYPE_CAMELLIA)
+ case PSA_KEY_TYPE_CAMELLIA:
+ if (bits != 128 && bits != 192 && bits != 256) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+ break;
+#endif
+#if defined(PSA_WANT_KEY_TYPE_DES)
+ case PSA_KEY_TYPE_DES:
+ if (bits != 64 && bits != 128 && bits != 192) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+ break;
+#endif
+#if defined(PSA_WANT_KEY_TYPE_CHACHA20)
+ case PSA_KEY_TYPE_CHACHA20:
+ if (bits != 256) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+ break;
+#endif
+ default:
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+ if (bits % 8 != 0) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ return PSA_SUCCESS;
+}
+
+/** Check whether a given key type is valid for use with a given MAC algorithm
+ *
+ * Upon successful return of this function, the behavior of #PSA_MAC_LENGTH
+ * when called with the validated \p algorithm and \p key_type is well-defined.
+ *
+ * \param[in] algorithm The specific MAC algorithm (can be wildcard).
+ * \param[in] key_type The key type of the key to be used with the
+ * \p algorithm.
+ *
+ * \retval #PSA_SUCCESS
+ * The \p key_type is valid for use with the \p algorithm
+ * \retval #PSA_ERROR_INVALID_ARGUMENT
+ * The \p key_type is not valid for use with the \p algorithm
+ */
+MBEDTLS_STATIC_TESTABLE psa_status_t psa_mac_key_can_do(
+ psa_algorithm_t algorithm,
+ psa_key_type_t key_type)
+{
+ if (PSA_ALG_IS_HMAC(algorithm)) {
+ if (key_type == PSA_KEY_TYPE_HMAC) {
+ return PSA_SUCCESS;
+ }
+ }
+
+ if (PSA_ALG_IS_BLOCK_CIPHER_MAC(algorithm)) {
+ /* Check that we're calling PSA_BLOCK_CIPHER_BLOCK_LENGTH with a cipher
+ * key. */
+ if ((key_type & PSA_KEY_TYPE_CATEGORY_MASK) ==
+ PSA_KEY_TYPE_CATEGORY_SYMMETRIC) {
+ /* PSA_BLOCK_CIPHER_BLOCK_LENGTH returns 1 for stream ciphers and
+ * the block length (larger than 1) for block ciphers. */
+ if (PSA_BLOCK_CIPHER_BLOCK_LENGTH(key_type) > 1) {
+ return PSA_SUCCESS;
+ }
+ }
+ }
+
+ return PSA_ERROR_INVALID_ARGUMENT;
+}
+
+psa_status_t psa_allocate_buffer_to_slot(psa_key_slot_t *slot,
+ size_t buffer_length)
+{
+ if (slot->key.data != NULL) {
+ return PSA_ERROR_ALREADY_EXISTS;
+ }
+
+ slot->key.data = mbedtls_calloc(1, buffer_length);
+ if (slot->key.data == NULL) {
+ return PSA_ERROR_INSUFFICIENT_MEMORY;
+ }
+
+ slot->key.bytes = buffer_length;
+ return PSA_SUCCESS;
+}
+
+psa_status_t psa_copy_key_material_into_slot(psa_key_slot_t *slot,
+ const uint8_t *data,
+ size_t data_length)
+{
+ psa_status_t status = psa_allocate_buffer_to_slot(slot,
+ data_length);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ memcpy(slot->key.data, data, data_length);
+ return PSA_SUCCESS;
+}
+
+psa_status_t psa_import_key_into_slot(
+ const psa_key_attributes_t *attributes,
+ const uint8_t *data, size_t data_length,
+ uint8_t *key_buffer, size_t key_buffer_size,
+ size_t *key_buffer_length, size_t *bits)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_key_type_t type = attributes->type;
+
+ /* zero-length keys are never supported. */
+ if (data_length == 0) {
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+
+ if (key_type_is_raw_bytes(type)) {
+ *bits = PSA_BYTES_TO_BITS(data_length);
+
+ status = psa_validate_unstructured_key_bit_size(attributes->type,
+ *bits);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ /* Copy the key material. */
+ memcpy(key_buffer, data, data_length);
+ *key_buffer_length = data_length;
+ (void) key_buffer_size;
+
+ return PSA_SUCCESS;
+ } else if (PSA_KEY_TYPE_IS_ASYMMETRIC(type)) {
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_KEY_PAIR_IMPORT) || \
+ defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_PUBLIC_KEY)
+ if (PSA_KEY_TYPE_IS_DH(type)) {
+ if (psa_is_dh_key_size_valid(PSA_BYTES_TO_BITS(data_length)) == 0) {
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+ return mbedtls_psa_ffdh_import_key(attributes,
+ data, data_length,
+ key_buffer, key_buffer_size,
+ key_buffer_length,
+ bits);
+ }
+#endif /* defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_KEY_PAIR_IMPORT) ||
+ * defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_PUBLIC_KEY) */
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR_IMPORT) || \
+ defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_PUBLIC_KEY)
+ if (PSA_KEY_TYPE_IS_ECC(type)) {
+ return mbedtls_psa_ecp_import_key(attributes,
+ data, data_length,
+ key_buffer, key_buffer_size,
+ key_buffer_length,
+ bits);
+ }
+#endif /* defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR_IMPORT) ||
+ * defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_PUBLIC_KEY) */
+#if (defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR_IMPORT) && \
+ defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR_EXPORT)) || \
+ defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_PUBLIC_KEY)
+ if (PSA_KEY_TYPE_IS_RSA(type)) {
+ return mbedtls_psa_rsa_import_key(attributes,
+ data, data_length,
+ key_buffer, key_buffer_size,
+ key_buffer_length,
+ bits);
+ }
+#endif /* (defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR_IMPORT) &&
+ defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR_EXPORT)) ||
+ * defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_PUBLIC_KEY) */
+ }
+
+ return PSA_ERROR_NOT_SUPPORTED;
+}
+
+/** Calculate the intersection of two algorithm usage policies.
+ *
+ * Return 0 (which allows no operation) on incompatibility.
+ */
+static psa_algorithm_t psa_key_policy_algorithm_intersection(
+ psa_key_type_t key_type,
+ psa_algorithm_t alg1,
+ psa_algorithm_t alg2)
+{
+ /* Common case: both sides actually specify the same policy. */
+ if (alg1 == alg2) {
+ return alg1;
+ }
+ /* If the policies are from the same hash-and-sign family, check
+ * if one is a wildcard. If so the other has the specific algorithm. */
+ if (PSA_ALG_IS_SIGN_HASH(alg1) &&
+ PSA_ALG_IS_SIGN_HASH(alg2) &&
+ (alg1 & ~PSA_ALG_HASH_MASK) == (alg2 & ~PSA_ALG_HASH_MASK)) {
+ if (PSA_ALG_SIGN_GET_HASH(alg1) == PSA_ALG_ANY_HASH) {
+ return alg2;
+ }
+ if (PSA_ALG_SIGN_GET_HASH(alg2) == PSA_ALG_ANY_HASH) {
+ return alg1;
+ }
+ }
+ /* If the policies are from the same AEAD family, check whether
+ * one of them is a minimum-tag-length wildcard. Calculate the most
+ * restrictive tag length. */
+ if (PSA_ALG_IS_AEAD(alg1) && PSA_ALG_IS_AEAD(alg2) &&
+ (PSA_ALG_AEAD_WITH_SHORTENED_TAG(alg1, 0) ==
+ PSA_ALG_AEAD_WITH_SHORTENED_TAG(alg2, 0))) {
+ size_t alg1_len = PSA_ALG_AEAD_GET_TAG_LENGTH(alg1);
+ size_t alg2_len = PSA_ALG_AEAD_GET_TAG_LENGTH(alg2);
+ size_t restricted_len = alg1_len > alg2_len ? alg1_len : alg2_len;
+
+ /* If both are wildcards, return most restrictive wildcard */
+ if (((alg1 & PSA_ALG_AEAD_AT_LEAST_THIS_LENGTH_FLAG) != 0) &&
+ ((alg2 & PSA_ALG_AEAD_AT_LEAST_THIS_LENGTH_FLAG) != 0)) {
+ return PSA_ALG_AEAD_WITH_AT_LEAST_THIS_LENGTH_TAG(
+ alg1, restricted_len);
+ }
+ /* If only one is a wildcard, return specific algorithm if compatible. */
+ if (((alg1 & PSA_ALG_AEAD_AT_LEAST_THIS_LENGTH_FLAG) != 0) &&
+ (alg1_len <= alg2_len)) {
+ return alg2;
+ }
+ if (((alg2 & PSA_ALG_AEAD_AT_LEAST_THIS_LENGTH_FLAG) != 0) &&
+ (alg2_len <= alg1_len)) {
+ return alg1;
+ }
+ }
+ /* If the policies are from the same MAC family, check whether one
+ * of them is a minimum-MAC-length policy. Calculate the most
+ * restrictive tag length. */
+ if (PSA_ALG_IS_MAC(alg1) && PSA_ALG_IS_MAC(alg2) &&
+ (PSA_ALG_FULL_LENGTH_MAC(alg1) ==
+ PSA_ALG_FULL_LENGTH_MAC(alg2))) {
+ /* Validate the combination of key type and algorithm. Since the base
+ * algorithm of alg1 and alg2 are the same, we only need this once. */
+ if (PSA_SUCCESS != psa_mac_key_can_do(alg1, key_type)) {
+ return 0;
+ }
+
+ /* Get the (exact or at-least) output lengths for both sides of the
+ * requested intersection. None of the currently supported algorithms
+ * have an output length dependent on the actual key size, so setting it
+ * to a bogus value of 0 is currently OK.
+ *
+ * Note that for at-least-this-length wildcard algorithms, the output
+ * length is set to the shortest allowed length, which allows us to
+ * calculate the most restrictive tag length for the intersection. */
+ size_t alg1_len = PSA_MAC_LENGTH(key_type, 0, alg1);
+ size_t alg2_len = PSA_MAC_LENGTH(key_type, 0, alg2);
+ size_t restricted_len = alg1_len > alg2_len ? alg1_len : alg2_len;
+
+ /* If both are wildcards, return most restrictive wildcard */
+ if (((alg1 & PSA_ALG_MAC_AT_LEAST_THIS_LENGTH_FLAG) != 0) &&
+ ((alg2 & PSA_ALG_MAC_AT_LEAST_THIS_LENGTH_FLAG) != 0)) {
+ return PSA_ALG_AT_LEAST_THIS_LENGTH_MAC(alg1, restricted_len);
+ }
+
+ /* If only one is an at-least-this-length policy, the intersection would
+ * be the other (fixed-length) policy as long as said fixed length is
+ * equal to or larger than the shortest allowed length. */
+ if ((alg1 & PSA_ALG_MAC_AT_LEAST_THIS_LENGTH_FLAG) != 0) {
+ return (alg1_len <= alg2_len) ? alg2 : 0;
+ }
+ if ((alg2 & PSA_ALG_MAC_AT_LEAST_THIS_LENGTH_FLAG) != 0) {
+ return (alg2_len <= alg1_len) ? alg1 : 0;
+ }
+
+ /* If none of them are wildcards, check whether they define the same tag
+ * length. This is still possible here when one is default-length and
+ * the other specific-length. Ensure to always return the
+ * specific-length version for the intersection. */
+ if (alg1_len == alg2_len) {
+ return PSA_ALG_TRUNCATED_MAC(alg1, alg1_len);
+ }
+ }
+ /* If the policies are incompatible, allow nothing. */
+ return 0;
+}
+
+static int psa_key_algorithm_permits(psa_key_type_t key_type,
+ psa_algorithm_t policy_alg,
+ psa_algorithm_t requested_alg)
+{
+ /* Common case: the policy only allows requested_alg. */
+ if (requested_alg == policy_alg) {
+ return 1;
+ }
+ /* If policy_alg is a hash-and-sign with a wildcard for the hash,
+ * and requested_alg is the same hash-and-sign family with any hash,
+ * then requested_alg is compliant with policy_alg. */
+ if (PSA_ALG_IS_SIGN_HASH(requested_alg) &&
+ PSA_ALG_SIGN_GET_HASH(policy_alg) == PSA_ALG_ANY_HASH) {
+ return (policy_alg & ~PSA_ALG_HASH_MASK) ==
+ (requested_alg & ~PSA_ALG_HASH_MASK);
+ }
+ /* If policy_alg is a wildcard AEAD algorithm of the same base as
+ * the requested algorithm, check the requested tag length to be
+ * equal-length or longer than the wildcard-specified length. */
+ if (PSA_ALG_IS_AEAD(policy_alg) &&
+ PSA_ALG_IS_AEAD(requested_alg) &&
+ (PSA_ALG_AEAD_WITH_SHORTENED_TAG(policy_alg, 0) ==
+ PSA_ALG_AEAD_WITH_SHORTENED_TAG(requested_alg, 0)) &&
+ ((policy_alg & PSA_ALG_AEAD_AT_LEAST_THIS_LENGTH_FLAG) != 0)) {
+ return PSA_ALG_AEAD_GET_TAG_LENGTH(policy_alg) <=
+ PSA_ALG_AEAD_GET_TAG_LENGTH(requested_alg);
+ }
+ /* If policy_alg is a MAC algorithm of the same base as the requested
+ * algorithm, check whether their MAC lengths are compatible. */
+ if (PSA_ALG_IS_MAC(policy_alg) &&
+ PSA_ALG_IS_MAC(requested_alg) &&
+ (PSA_ALG_FULL_LENGTH_MAC(policy_alg) ==
+ PSA_ALG_FULL_LENGTH_MAC(requested_alg))) {
+ /* Validate the combination of key type and algorithm. Since the policy
+ * and requested algorithms are the same, we only need this once. */
+ if (PSA_SUCCESS != psa_mac_key_can_do(policy_alg, key_type)) {
+ return 0;
+ }
+
+ /* Get both the requested output length for the algorithm which is to be
+ * verified, and the default output length for the base algorithm.
+ * Note that none of the currently supported algorithms have an output
+ * length dependent on actual key size, so setting it to a bogus value
+ * of 0 is currently OK. */
+ size_t requested_output_length = PSA_MAC_LENGTH(
+ key_type, 0, requested_alg);
+ size_t default_output_length = PSA_MAC_LENGTH(
+ key_type, 0,
+ PSA_ALG_FULL_LENGTH_MAC(requested_alg));
+
+ /* If the policy is default-length, only allow an algorithm with
+ * a declared exact-length matching the default. */
+ if (PSA_MAC_TRUNCATED_LENGTH(policy_alg) == 0) {
+ return requested_output_length == default_output_length;
+ }
+
+ /* If the requested algorithm is default-length, allow it if the policy
+ * length exactly matches the default length. */
+ if (PSA_MAC_TRUNCATED_LENGTH(requested_alg) == 0 &&
+ PSA_MAC_TRUNCATED_LENGTH(policy_alg) == default_output_length) {
+ return 1;
+ }
+
+ /* If policy_alg is an at-least-this-length wildcard MAC algorithm,
+ * check for the requested MAC length to be equal to or longer than the
+ * minimum allowed length. */
+ if ((policy_alg & PSA_ALG_MAC_AT_LEAST_THIS_LENGTH_FLAG) != 0) {
+ return PSA_MAC_TRUNCATED_LENGTH(policy_alg) <=
+ requested_output_length;
+ }
+ }
+ /* If policy_alg is a generic key agreement operation, then using it for
+ * a key derivation with that key agreement should also be allowed. This
+ * behaviour is expected to be defined in a future specification version. */
+ if (PSA_ALG_IS_RAW_KEY_AGREEMENT(policy_alg) &&
+ PSA_ALG_IS_KEY_AGREEMENT(requested_alg)) {
+ return PSA_ALG_KEY_AGREEMENT_GET_BASE(requested_alg) ==
+ policy_alg;
+ }
+ /* If it isn't explicitly permitted, it's forbidden. */
+ return 0;
+}
+
+/** Test whether a policy permits an algorithm.
+ *
+ * The caller must test usage flags separately.
+ *
+ * \note This function requires providing the key type for which the policy is
+ * being validated, since some algorithm policy definitions (e.g. MAC)
+ * have different properties depending on what kind of cipher it is
+ * combined with.
+ *
+ * \retval PSA_SUCCESS When \p alg is a specific algorithm
+ * allowed by the \p policy.
+ * \retval PSA_ERROR_INVALID_ARGUMENT When \p alg is not a specific algorithm
+ * \retval PSA_ERROR_NOT_PERMITTED When \p alg is a specific algorithm, but
+ * the \p policy does not allow it.
+ */
+static psa_status_t psa_key_policy_permits(const psa_key_policy_t *policy,
+ psa_key_type_t key_type,
+ psa_algorithm_t alg)
+{
+ /* '0' is not a valid algorithm */
+ if (alg == 0) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ /* A requested algorithm cannot be a wildcard. */
+ if (PSA_ALG_IS_WILDCARD(alg)) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ if (psa_key_algorithm_permits(key_type, policy->alg, alg) ||
+ psa_key_algorithm_permits(key_type, policy->alg2, alg)) {
+ return PSA_SUCCESS;
+ } else {
+ return PSA_ERROR_NOT_PERMITTED;
+ }
+}
+
+/** Restrict a key policy based on a constraint.
+ *
+ * \note This function requires providing the key type for which the policy is
+ * being restricted, since some algorithm policy definitions (e.g. MAC)
+ * have different properties depending on what kind of cipher it is
+ * combined with.
+ *
+ * \param[in] key_type The key type for which to restrict the policy
+ * \param[in,out] policy The policy to restrict.
+ * \param[in] constraint The policy constraint to apply.
+ *
+ * \retval #PSA_SUCCESS
+ * \c *policy contains the intersection of the original value of
+ * \c *policy and \c *constraint.
+ * \retval #PSA_ERROR_INVALID_ARGUMENT
+ * \c key_type, \c *policy and \c *constraint are incompatible.
+ * \c *policy is unchanged.
+ */
+static psa_status_t psa_restrict_key_policy(
+ psa_key_type_t key_type,
+ psa_key_policy_t *policy,
+ const psa_key_policy_t *constraint)
+{
+ psa_algorithm_t intersection_alg =
+ psa_key_policy_algorithm_intersection(key_type, policy->alg,
+ constraint->alg);
+ psa_algorithm_t intersection_alg2 =
+ psa_key_policy_algorithm_intersection(key_type, policy->alg2,
+ constraint->alg2);
+ if (intersection_alg == 0 && policy->alg != 0 && constraint->alg != 0) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+ if (intersection_alg2 == 0 && policy->alg2 != 0 && constraint->alg2 != 0) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+ policy->usage &= constraint->usage;
+ policy->alg = intersection_alg;
+ policy->alg2 = intersection_alg2;
+ return PSA_SUCCESS;
+}
+
+/** Get the description of a key given its identifier and policy constraints
+ * and lock it.
+ *
+ * The key must have allow all the usage flags set in \p usage. If \p alg is
+ * nonzero, the key must allow operations with this algorithm. If \p alg is
+ * zero, the algorithm is not checked.
+ *
+ * In case of a persistent key, the function loads the description of the key
+ * into a key slot if not already done.
+ *
+ * On success, the returned key slot has been registered for reading.
+ * It is the responsibility of the caller to then unregister
+ * once they have finished reading the contents of the slot.
+ * The caller unregisters by calling psa_unregister_read() or
+ * psa_unregister_read_under_mutex(). psa_unregister_read() must be called
+ * if and only if the caller already holds the global key slot mutex
+ * (when mutexes are enabled). psa_unregister_read_under_mutex() encapsulates
+ * the unregister with mutex lock and unlock operations.
+ */
+static psa_status_t psa_get_and_lock_key_slot_with_policy(
+ mbedtls_svc_key_id_t key,
+ psa_key_slot_t **p_slot,
+ psa_key_usage_t usage,
+ psa_algorithm_t alg)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_key_slot_t *slot = NULL;
+
+ status = psa_get_and_lock_key_slot(key, p_slot);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+ slot = *p_slot;
+
+ /* Enforce that usage policy for the key slot contains all the flags
+ * required by the usage parameter. There is one exception: public
+ * keys can always be exported, so we treat public key objects as
+ * if they had the export flag. */
+ if (PSA_KEY_TYPE_IS_PUBLIC_KEY(slot->attr.type)) {
+ usage &= ~PSA_KEY_USAGE_EXPORT;
+ }
+
+ if ((slot->attr.policy.usage & usage) != usage) {
+ status = PSA_ERROR_NOT_PERMITTED;
+ goto error;
+ }
+
+ /* Enforce that the usage policy permits the requested algorithm. */
+ if (alg != 0) {
+ status = psa_key_policy_permits(&slot->attr.policy,
+ slot->attr.type,
+ alg);
+ if (status != PSA_SUCCESS) {
+ goto error;
+ }
+ }
+
+ return PSA_SUCCESS;
+
+error:
+ *p_slot = NULL;
+ psa_unregister_read_under_mutex(slot);
+
+ return status;
+}
+
+/** Get a key slot containing a transparent key and lock it.
+ *
+ * A transparent key is a key for which the key material is directly
+ * available, as opposed to a key in a secure element and/or to be used
+ * by a secure element.
+ *
+ * This is a temporary function that may be used instead of
+ * psa_get_and_lock_key_slot_with_policy() when there is no opaque key support
+ * for a cryptographic operation.
+ *
+ * On success, the returned key slot has been registered for reading.
+ * It is the responsibility of the caller to then unregister
+ * once they have finished reading the contents of the slot.
+ * The caller unregisters by calling psa_unregister_read() or
+ * psa_unregister_read_under_mutex(). psa_unregister_read() must be called
+ * if and only if the caller already holds the global key slot mutex
+ * (when mutexes are enabled). psa_unregister_read_under_mutex() encapsulates
+ * psa_unregister_read() with mutex lock and unlock operations.
+ */
+static psa_status_t psa_get_and_lock_transparent_key_slot_with_policy(
+ mbedtls_svc_key_id_t key,
+ psa_key_slot_t **p_slot,
+ psa_key_usage_t usage,
+ psa_algorithm_t alg)
+{
+ psa_status_t status = psa_get_and_lock_key_slot_with_policy(key, p_slot,
+ usage, alg);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ if (psa_key_lifetime_is_external((*p_slot)->attr.lifetime)) {
+ psa_unregister_read_under_mutex(*p_slot);
+ *p_slot = NULL;
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+
+ return PSA_SUCCESS;
+}
+
+psa_status_t psa_remove_key_data_from_memory(psa_key_slot_t *slot)
+{
+ if (slot->key.data != NULL) {
+ mbedtls_zeroize_and_free(slot->key.data, slot->key.bytes);
+ }
+
+ slot->key.data = NULL;
+ slot->key.bytes = 0;
+
+ return PSA_SUCCESS;
+}
+
+/** Completely wipe a slot in memory, including its policy.
+ * Persistent storage is not affected. */
+psa_status_t psa_wipe_key_slot(psa_key_slot_t *slot)
+{
+ psa_status_t status = psa_remove_key_data_from_memory(slot);
+
+ /*
+ * As the return error code may not be handled in case of multiple errors,
+ * do our best to report an unexpected amount of registered readers or
+ * an unexpected state.
+ * Assert with MBEDTLS_TEST_HOOK_TEST_ASSERT that the slot is valid for
+ * wiping.
+ * if the MBEDTLS_TEST_HOOKS configuration option is enabled and the
+ * function is called as part of the execution of a test suite, the
+ * execution of the test suite is stopped in error if the assertion fails.
+ */
+ switch (slot->state) {
+ case PSA_SLOT_FULL:
+ /* In this state psa_wipe_key_slot() must only be called if the
+ * caller is the last reader. */
+ case PSA_SLOT_PENDING_DELETION:
+ /* In this state psa_wipe_key_slot() must only be called if the
+ * caller is the last reader. */
+ if (slot->var.occupied.registered_readers != 1) {
+ MBEDTLS_TEST_HOOK_TEST_ASSERT(slot->var.occupied.registered_readers == 1);
+ status = PSA_ERROR_CORRUPTION_DETECTED;
+ }
+ break;
+ case PSA_SLOT_FILLING:
+ /* In this state registered_readers must be 0. */
+ if (slot->var.occupied.registered_readers != 0) {
+ MBEDTLS_TEST_HOOK_TEST_ASSERT(slot->var.occupied.registered_readers == 0);
+ status = PSA_ERROR_CORRUPTION_DETECTED;
+ }
+ break;
+ case PSA_SLOT_EMPTY:
+ /* The slot is already empty, it cannot be wiped. */
+ MBEDTLS_TEST_HOOK_TEST_ASSERT(slot->state != PSA_SLOT_EMPTY);
+ status = PSA_ERROR_CORRUPTION_DETECTED;
+ break;
+ default:
+ /* The slot's state is invalid. */
+ status = PSA_ERROR_CORRUPTION_DETECTED;
+ }
+
+#if defined(MBEDTLS_PSA_KEY_STORE_DYNAMIC)
+ size_t slice_index = slot->slice_index;
+#endif /* MBEDTLS_PSA_KEY_STORE_DYNAMIC */
+
+
+ /* Multipart operations may still be using the key. This is safe
+ * because all multipart operation objects are independent from
+ * the key slot: if they need to access the key after the setup
+ * phase, they have a copy of the key. Note that this means that
+ * key material can linger until all operations are completed. */
+ /* At this point, key material and other type-specific content has
+ * been wiped. Clear remaining metadata. We can call memset and not
+ * zeroize because the metadata is not particularly sensitive.
+ * This memset also sets the slot's state to PSA_SLOT_EMPTY. */
+ memset(slot, 0, sizeof(*slot));
+
+#if defined(MBEDTLS_PSA_KEY_STORE_DYNAMIC)
+ /* If the slot is already corrupted, something went deeply wrong,
+ * like a thread still using the slot or a stray pointer leading
+ * to the slot's memory being used for another object. Let the slot
+ * leak rather than make the corruption worse. */
+ if (status == PSA_SUCCESS) {
+ status = psa_free_key_slot(slice_index, slot);
+ }
+#endif /* MBEDTLS_PSA_KEY_STORE_DYNAMIC */
+
+ return status;
+}
+
+psa_status_t psa_destroy_key(mbedtls_svc_key_id_t key)
+{
+ psa_key_slot_t *slot;
+ psa_status_t status; /* status of the last operation */
+ psa_status_t overall_status = PSA_SUCCESS;
+#if defined(MBEDTLS_PSA_CRYPTO_SE_C)
+ psa_se_drv_table_entry_t *driver;
+#endif /* MBEDTLS_PSA_CRYPTO_SE_C */
+
+ if (mbedtls_svc_key_id_is_null(key)) {
+ return PSA_SUCCESS;
+ }
+
+ /*
+ * Get the description of the key in a key slot, and register to read it.
+ * In the case of a persistent key, this will load the key description
+ * from persistent memory if not done yet.
+ * We cannot avoid this loading as without it we don't know if
+ * the key is operated by an SE or not and this information is needed by
+ * the current implementation. */
+ status = psa_get_and_lock_key_slot(key, &slot);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+#if defined(MBEDTLS_THREADING_C)
+ /* We cannot unlock between setting the state to PENDING_DELETION
+ * and destroying the key in storage, as otherwise another thread
+ * could load the key into a new slot and the key will not be
+ * fully destroyed. */
+ PSA_THREADING_CHK_GOTO_EXIT(mbedtls_mutex_lock(
+ &mbedtls_threading_key_slot_mutex));
+
+ if (slot->state == PSA_SLOT_PENDING_DELETION) {
+ /* Another thread has destroyed the key between us locking the slot
+ * and us gaining the mutex. Unregister from the slot,
+ * and report that the key does not exist. */
+ status = psa_unregister_read(slot);
+
+ PSA_THREADING_CHK_RET(mbedtls_mutex_unlock(
+ &mbedtls_threading_key_slot_mutex));
+ return (status == PSA_SUCCESS) ? PSA_ERROR_INVALID_HANDLE : status;
+ }
+#endif
+ /* Set the key slot containing the key description's state to
+ * PENDING_DELETION. This stops new operations from registering
+ * to read the slot. Current readers can safely continue to access
+ * the key within the slot; the last registered reader will
+ * automatically wipe the slot when they call psa_unregister_read().
+ * If the key is persistent, we can now delete the copy of the key
+ * from memory. If the key is opaque, we require the driver to
+ * deal with the deletion. */
+ overall_status = psa_key_slot_state_transition(slot, PSA_SLOT_FULL,
+ PSA_SLOT_PENDING_DELETION);
+
+ if (overall_status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ if (PSA_KEY_LIFETIME_IS_READ_ONLY(slot->attr.lifetime)) {
+ /* Refuse the destruction of a read-only key (which may or may not work
+ * if we attempt it, depending on whether the key is merely read-only
+ * by policy or actually physically read-only).
+ * Just do the best we can, which is to wipe the copy in memory
+ * (done in this function's cleanup code). */
+ overall_status = PSA_ERROR_NOT_PERMITTED;
+ goto exit;
+ }
+
+#if defined(MBEDTLS_PSA_CRYPTO_SE_C)
+ driver = psa_get_se_driver_entry(slot->attr.lifetime);
+ if (driver != NULL) {
+ /* For a key in a secure element, we need to do three things:
+ * remove the key file in internal storage, destroy the
+ * key inside the secure element, and update the driver's
+ * persistent data. Start a transaction that will encompass these
+ * three actions. */
+ psa_crypto_prepare_transaction(PSA_CRYPTO_TRANSACTION_DESTROY_KEY);
+ psa_crypto_transaction.key.lifetime = slot->attr.lifetime;
+ psa_crypto_transaction.key.slot = psa_key_slot_get_slot_number(slot);
+ psa_crypto_transaction.key.id = slot->attr.id;
+ status = psa_crypto_save_transaction();
+ if (status != PSA_SUCCESS) {
+ (void) psa_crypto_stop_transaction();
+ /* We should still try to destroy the key in the secure
+ * element and the key metadata in storage. This is especially
+ * important if the error is that the storage is full.
+ * But how to do it exactly without risking an inconsistent
+ * state after a reset?
+ * https://github.com/ARMmbed/mbed-crypto/issues/215
+ */
+ overall_status = status;
+ goto exit;
+ }
+
+ status = psa_destroy_se_key(driver,
+ psa_key_slot_get_slot_number(slot));
+ if (overall_status == PSA_SUCCESS) {
+ overall_status = status;
+ }
+ }
+#endif /* MBEDTLS_PSA_CRYPTO_SE_C */
+
+#if defined(MBEDTLS_PSA_CRYPTO_STORAGE_C)
+ if (!PSA_KEY_LIFETIME_IS_VOLATILE(slot->attr.lifetime)) {
+ /* Destroy the copy of the persistent key from storage.
+ * The slot will still hold a copy of the key until the last reader
+ * unregisters. */
+ status = psa_destroy_persistent_key(slot->attr.id);
+ if (overall_status == PSA_SUCCESS) {
+ overall_status = status;
+ }
+ }
+#endif /* defined(MBEDTLS_PSA_CRYPTO_STORAGE_C) */
+
+#if defined(MBEDTLS_PSA_CRYPTO_SE_C)
+ if (driver != NULL) {
+ status = psa_save_se_persistent_data(driver);
+ if (overall_status == PSA_SUCCESS) {
+ overall_status = status;
+ }
+ status = psa_crypto_stop_transaction();
+ if (overall_status == PSA_SUCCESS) {
+ overall_status = status;
+ }
+ }
+#endif /* MBEDTLS_PSA_CRYPTO_SE_C */
+
+exit:
+ /* Unregister from reading the slot. If we are the last active reader
+ * then this will wipe the slot. */
+ status = psa_unregister_read(slot);
+ /* Prioritize CORRUPTION_DETECTED from unregistering over
+ * a storage error. */
+ if (status != PSA_SUCCESS) {
+ overall_status = status;
+ }
+
+#if defined(MBEDTLS_THREADING_C)
+ /* Don't overwrite existing errors if the unlock fails. */
+ status = overall_status;
+ PSA_THREADING_CHK_RET(mbedtls_mutex_unlock(
+ &mbedtls_threading_key_slot_mutex));
+#endif
+
+ return overall_status;
+}
+
+/** Retrieve all the publicly-accessible attributes of a key.
+ */
+psa_status_t psa_get_key_attributes(mbedtls_svc_key_id_t key,
+ psa_key_attributes_t *attributes)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_key_slot_t *slot;
+
+ psa_reset_key_attributes(attributes);
+
+ status = psa_get_and_lock_key_slot_with_policy(key, &slot, 0, 0);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ *attributes = slot->attr;
+
+#if defined(MBEDTLS_PSA_CRYPTO_SE_C)
+ if (psa_get_se_driver_entry(slot->attr.lifetime) != NULL) {
+ psa_set_key_slot_number(attributes,
+ psa_key_slot_get_slot_number(slot));
+ }
+#endif /* MBEDTLS_PSA_CRYPTO_SE_C */
+
+ return psa_unregister_read_under_mutex(slot);
+}
+
+#if defined(MBEDTLS_PSA_CRYPTO_SE_C)
+psa_status_t psa_get_key_slot_number(
+ const psa_key_attributes_t *attributes,
+ psa_key_slot_number_t *slot_number)
+{
+ if (attributes->has_slot_number) {
+ *slot_number = attributes->slot_number;
+ return PSA_SUCCESS;
+ } else {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+}
+#endif /* MBEDTLS_PSA_CRYPTO_SE_C */
+
+static psa_status_t psa_export_key_buffer_internal(const uint8_t *key_buffer,
+ size_t key_buffer_size,
+ uint8_t *data,
+ size_t data_size,
+ size_t *data_length)
+{
+ if (key_buffer_size > data_size) {
+ return PSA_ERROR_BUFFER_TOO_SMALL;
+ }
+ memcpy(data, key_buffer, key_buffer_size);
+ memset(data + key_buffer_size, 0,
+ data_size - key_buffer_size);
+ *data_length = key_buffer_size;
+ return PSA_SUCCESS;
+}
+
+psa_status_t psa_export_key_internal(
+ const psa_key_attributes_t *attributes,
+ const uint8_t *key_buffer, size_t key_buffer_size,
+ uint8_t *data, size_t data_size, size_t *data_length)
+{
+ psa_key_type_t type = attributes->type;
+
+ if (key_type_is_raw_bytes(type) ||
+ PSA_KEY_TYPE_IS_RSA(type) ||
+ PSA_KEY_TYPE_IS_ECC(type) ||
+ PSA_KEY_TYPE_IS_DH(type)) {
+ return psa_export_key_buffer_internal(
+ key_buffer, key_buffer_size,
+ data, data_size, data_length);
+ } else {
+ /* This shouldn't happen in the reference implementation, but
+ it is valid for a special-purpose implementation to omit
+ support for exporting certain key types. */
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+}
+
+psa_status_t psa_export_key(mbedtls_svc_key_id_t key,
+ uint8_t *data_external,
+ size_t data_size,
+ size_t *data_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_key_slot_t *slot;
+ LOCAL_OUTPUT_DECLARE(data_external, data);
+
+ /* Reject a zero-length output buffer now, since this can never be a
+ * valid key representation. This way we know that data must be a valid
+ * pointer and we can do things like memset(data, ..., data_size). */
+ if (data_size == 0) {
+ return PSA_ERROR_BUFFER_TOO_SMALL;
+ }
+
+ /* Set the key to empty now, so that even when there are errors, we always
+ * set data_length to a value between 0 and data_size. On error, setting
+ * the key to empty is a good choice because an empty key representation is
+ * unlikely to be accepted anywhere. */
+ *data_length = 0;
+
+ /* Export requires the EXPORT flag. There is an exception for public keys,
+ * which don't require any flag, but
+ * psa_get_and_lock_key_slot_with_policy() takes care of this.
+ */
+ status = psa_get_and_lock_key_slot_with_policy(key, &slot,
+ PSA_KEY_USAGE_EXPORT, 0);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ LOCAL_OUTPUT_ALLOC(data_external, data_size, data);
+
+ status = psa_driver_wrapper_export_key(&slot->attr,
+ slot->key.data, slot->key.bytes,
+ data, data_size, data_length);
+
+#if !defined(MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS)
+exit:
+#endif
+ unlock_status = psa_unregister_read_under_mutex(slot);
+
+ LOCAL_OUTPUT_FREE(data_external, data);
+ return (status == PSA_SUCCESS) ? unlock_status : status;
+}
+
+psa_status_t psa_export_public_key_internal(
+ const psa_key_attributes_t *attributes,
+ const uint8_t *key_buffer,
+ size_t key_buffer_size,
+ uint8_t *data,
+ size_t data_size,
+ size_t *data_length)
+{
+ psa_key_type_t type = attributes->type;
+
+ if (PSA_KEY_TYPE_IS_PUBLIC_KEY(type) &&
+ (PSA_KEY_TYPE_IS_RSA(type) || PSA_KEY_TYPE_IS_ECC(type) ||
+ PSA_KEY_TYPE_IS_DH(type))) {
+ /* Exporting public -> public */
+ return psa_export_key_buffer_internal(
+ key_buffer, key_buffer_size,
+ data, data_size, data_length);
+ } else if (PSA_KEY_TYPE_IS_RSA(type)) {
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR_EXPORT) || \
+ defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_PUBLIC_KEY)
+ return mbedtls_psa_rsa_export_public_key(attributes,
+ key_buffer,
+ key_buffer_size,
+ data,
+ data_size,
+ data_length);
+#else
+ /* We don't know how to convert a private RSA key to public. */
+ return PSA_ERROR_NOT_SUPPORTED;
+#endif /* defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR_EXPORT) ||
+ * defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_PUBLIC_KEY) */
+ } else if (PSA_KEY_TYPE_IS_ECC(type)) {
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR_EXPORT) || \
+ defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_PUBLIC_KEY)
+ return mbedtls_psa_ecp_export_public_key(attributes,
+ key_buffer,
+ key_buffer_size,
+ data,
+ data_size,
+ data_length);
+#else
+ /* We don't know how to convert a private ECC key to public */
+ return PSA_ERROR_NOT_SUPPORTED;
+#endif /* defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR_EXPORT) ||
+ * defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_PUBLIC_KEY) */
+ } else if (PSA_KEY_TYPE_IS_DH(type)) {
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_KEY_PAIR_EXPORT) || \
+ defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_PUBLIC_KEY)
+ return mbedtls_psa_ffdh_export_public_key(attributes,
+ key_buffer,
+ key_buffer_size,
+ data, data_size,
+ data_length);
+#else
+ return PSA_ERROR_NOT_SUPPORTED;
+#endif /* defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_KEY_PAIR_EXPORT) ||
+ * defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_PUBLIC_KEY) */
+ } else {
+ (void) key_buffer;
+ (void) key_buffer_size;
+ (void) data;
+ (void) data_size;
+ (void) data_length;
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+}
+
+psa_status_t psa_export_public_key(mbedtls_svc_key_id_t key,
+ uint8_t *data_external,
+ size_t data_size,
+ size_t *data_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_key_slot_t *slot;
+
+ LOCAL_OUTPUT_DECLARE(data_external, data);
+
+ /* Reject a zero-length output buffer now, since this can never be a
+ * valid key representation. This way we know that data must be a valid
+ * pointer and we can do things like memset(data, ..., data_size). */
+ if (data_size == 0) {
+ return PSA_ERROR_BUFFER_TOO_SMALL;
+ }
+
+ /* Set the key to empty now, so that even when there are errors, we always
+ * set data_length to a value between 0 and data_size. On error, setting
+ * the key to empty is a good choice because an empty key representation is
+ * unlikely to be accepted anywhere. */
+ *data_length = 0;
+
+ /* Exporting a public key doesn't require a usage flag. */
+ status = psa_get_and_lock_key_slot_with_policy(key, &slot, 0, 0);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ LOCAL_OUTPUT_ALLOC(data_external, data_size, data);
+
+ if (!PSA_KEY_TYPE_IS_ASYMMETRIC(slot->attr.type)) {
+ status = PSA_ERROR_INVALID_ARGUMENT;
+ goto exit;
+ }
+
+ status = psa_driver_wrapper_export_public_key(
+ &slot->attr, slot->key.data, slot->key.bytes,
+ data, data_size, data_length);
+
+exit:
+ unlock_status = psa_unregister_read_under_mutex(slot);
+
+ LOCAL_OUTPUT_FREE(data_external, data);
+ return (status == PSA_SUCCESS) ? unlock_status : status;
+}
+
+/** Validate that a key policy is internally well-formed.
+ *
+ * This function only rejects invalid policies. It does not validate the
+ * consistency of the policy with respect to other attributes of the key
+ * such as the key type.
+ */
+static psa_status_t psa_validate_key_policy(const psa_key_policy_t *policy)
+{
+ if ((policy->usage & ~(PSA_KEY_USAGE_EXPORT |
+ PSA_KEY_USAGE_COPY |
+ PSA_KEY_USAGE_ENCRYPT |
+ PSA_KEY_USAGE_DECRYPT |
+ PSA_KEY_USAGE_SIGN_MESSAGE |
+ PSA_KEY_USAGE_VERIFY_MESSAGE |
+ PSA_KEY_USAGE_SIGN_HASH |
+ PSA_KEY_USAGE_VERIFY_HASH |
+ PSA_KEY_USAGE_VERIFY_DERIVATION |
+ PSA_KEY_USAGE_DERIVE)) != 0) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ return PSA_SUCCESS;
+}
+
+/** Validate the internal consistency of key attributes.
+ *
+ * This function only rejects invalid attribute values. If does not
+ * validate the consistency of the attributes with any key data that may
+ * be involved in the creation of the key.
+ *
+ * Call this function early in the key creation process.
+ *
+ * \param[in] attributes Key attributes for the new key.
+ * \param[out] p_drv On any return, the driver for the key, if any.
+ * NULL for a transparent key.
+ *
+ */
+static psa_status_t psa_validate_key_attributes(
+ const psa_key_attributes_t *attributes,
+ psa_se_drv_table_entry_t **p_drv)
+{
+ psa_status_t status = PSA_ERROR_INVALID_ARGUMENT;
+ psa_key_lifetime_t lifetime = psa_get_key_lifetime(attributes);
+ mbedtls_svc_key_id_t key = psa_get_key_id(attributes);
+
+ status = psa_validate_key_location(lifetime, p_drv);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ status = psa_validate_key_persistence(lifetime);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ if (PSA_KEY_LIFETIME_IS_VOLATILE(lifetime)) {
+ if (MBEDTLS_SVC_KEY_ID_GET_KEY_ID(key) != 0) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+ } else {
+ if (!psa_is_valid_key_id(psa_get_key_id(attributes), 0)) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+ }
+
+ status = psa_validate_key_policy(&attributes->policy);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ /* Refuse to create overly large keys.
+ * Note that this doesn't trigger on import if the attributes don't
+ * explicitly specify a size (so psa_get_key_bits returns 0), so
+ * psa_import_key() needs its own checks. */
+ if (psa_get_key_bits(attributes) > PSA_MAX_KEY_BITS) {
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+
+ return PSA_SUCCESS;
+}
+
+/** Prepare a key slot to receive key material.
+ *
+ * This function allocates a key slot and sets its metadata.
+ *
+ * If this function fails, call psa_fail_key_creation().
+ *
+ * This function is intended to be used as follows:
+ * -# Call psa_start_key_creation() to allocate a key slot, prepare
+ * it with the specified attributes, and in case of a volatile key assign it
+ * a volatile key identifier.
+ * -# Populate the slot with the key material.
+ * -# Call psa_finish_key_creation() to finalize the creation of the slot.
+ * In case of failure at any step, stop the sequence and call
+ * psa_fail_key_creation().
+ *
+ * On success, the key slot's state is PSA_SLOT_FILLING.
+ * It is the responsibility of the caller to change the slot's state to
+ * PSA_SLOT_EMPTY/FULL once key creation has finished.
+ *
+ * \param method An identification of the calling function.
+ * \param[in] attributes Key attributes for the new key.
+ * \param[out] p_slot On success, a pointer to the prepared slot.
+ * \param[out] p_drv On any return, the driver for the key, if any.
+ * NULL for a transparent key.
+ *
+ * \retval #PSA_SUCCESS
+ * The key slot is ready to receive key material.
+ * \return If this function fails, the key slot is an invalid state.
+ * You must call psa_fail_key_creation() to wipe and free the slot.
+ */
+static psa_status_t psa_start_key_creation(
+ psa_key_creation_method_t method,
+ const psa_key_attributes_t *attributes,
+ psa_key_slot_t **p_slot,
+ psa_se_drv_table_entry_t **p_drv)
+{
+ psa_status_t status;
+
+ (void) method;
+ *p_drv = NULL;
+
+ status = psa_validate_key_attributes(attributes, p_drv);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ int key_is_volatile = PSA_KEY_LIFETIME_IS_VOLATILE(attributes->lifetime);
+ psa_key_id_t volatile_key_id;
+
+#if defined(MBEDTLS_THREADING_C)
+ PSA_THREADING_CHK_RET(mbedtls_mutex_lock(
+ &mbedtls_threading_key_slot_mutex));
+#endif
+ status = psa_reserve_free_key_slot(
+ key_is_volatile ? &volatile_key_id : NULL,
+ p_slot);
+#if defined(MBEDTLS_THREADING_C)
+ PSA_THREADING_CHK_RET(mbedtls_mutex_unlock(
+ &mbedtls_threading_key_slot_mutex));
+#endif
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+ psa_key_slot_t *slot = *p_slot;
+
+ /* We're storing the declared bit-size of the key. It's up to each
+ * creation mechanism to verify that this information is correct.
+ * It's automatically correct for mechanisms that use the bit-size as
+ * an input (generate, device) but not for those where the bit-size
+ * is optional (import, copy). In case of a volatile key, assign it the
+ * volatile key identifier associated to the slot returned to contain its
+ * definition. */
+
+ slot->attr = *attributes;
+ if (key_is_volatile) {
+#if !defined(MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER)
+ slot->attr.id = volatile_key_id;
+#else
+ slot->attr.id.key_id = volatile_key_id;
+#endif
+ }
+
+#if defined(MBEDTLS_PSA_CRYPTO_SE_C)
+ /* For a key in a secure element, we need to do three things
+ * when creating or registering a persistent key:
+ * create the key file in internal storage, create the
+ * key inside the secure element, and update the driver's
+ * persistent data. This is done by starting a transaction that will
+ * encompass these three actions.
+ * For registering a volatile key, we just need to find an appropriate
+ * slot number inside the SE. Since the key is designated volatile, creating
+ * a transaction is not required. */
+ /* The first thing to do is to find a slot number for the new key.
+ * We save the slot number in persistent storage as part of the
+ * transaction data. It will be needed to recover if the power
+ * fails during the key creation process, to clean up on the secure
+ * element side after restarting. Obtaining a slot number from the
+ * secure element driver updates its persistent state, but we do not yet
+ * save the driver's persistent state, so that if the power fails,
+ * we can roll back to a state where the key doesn't exist. */
+ if (*p_drv != NULL) {
+ psa_key_slot_number_t slot_number;
+ status = psa_find_se_slot_for_key(attributes, method, *p_drv,
+ &slot_number);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ if (!PSA_KEY_LIFETIME_IS_VOLATILE(attributes->lifetime)) {
+ psa_crypto_prepare_transaction(PSA_CRYPTO_TRANSACTION_CREATE_KEY);
+ psa_crypto_transaction.key.lifetime = slot->attr.lifetime;
+ psa_crypto_transaction.key.slot = slot_number;
+ psa_crypto_transaction.key.id = slot->attr.id;
+ status = psa_crypto_save_transaction();
+ if (status != PSA_SUCCESS) {
+ (void) psa_crypto_stop_transaction();
+ return status;
+ }
+ }
+
+ status = psa_copy_key_material_into_slot(
+ slot, (uint8_t *) (&slot_number), sizeof(slot_number));
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+ }
+
+ if (*p_drv == NULL && method == PSA_KEY_CREATION_REGISTER) {
+ /* Key registration only makes sense with a secure element. */
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+#endif /* MBEDTLS_PSA_CRYPTO_SE_C */
+
+ return PSA_SUCCESS;
+}
+
+/** Finalize the creation of a key once its key material has been set.
+ *
+ * This entails writing the key to persistent storage.
+ *
+ * If this function fails, call psa_fail_key_creation().
+ * See the documentation of psa_start_key_creation() for the intended use
+ * of this function.
+ *
+ * If the finalization succeeds, the function sets the key slot's state to
+ * PSA_SLOT_FULL, and the key slot can no longer be accessed as part of the
+ * key creation process.
+ *
+ * \param[in,out] slot Pointer to the slot with key material.
+ * \param[in] driver The secure element driver for the key,
+ * or NULL for a transparent key.
+ * \param[out] key On success, identifier of the key. Note that the
+ * key identifier is also stored in the key slot.
+ *
+ * \retval #PSA_SUCCESS
+ * The key was successfully created.
+ * \retval #PSA_ERROR_INSUFFICIENT_MEMORY \emptydescription
+ * \retval #PSA_ERROR_INSUFFICIENT_STORAGE \emptydescription
+ * \retval #PSA_ERROR_ALREADY_EXISTS \emptydescription
+ * \retval #PSA_ERROR_DATA_INVALID \emptydescription
+ * \retval #PSA_ERROR_DATA_CORRUPT \emptydescription
+ * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription
+ *
+ * \return If this function fails, the key slot is an invalid state.
+ * You must call psa_fail_key_creation() to wipe and free the slot.
+ */
+static psa_status_t psa_finish_key_creation(
+ psa_key_slot_t *slot,
+ psa_se_drv_table_entry_t *driver,
+ mbedtls_svc_key_id_t *key)
+{
+ psa_status_t status = PSA_SUCCESS;
+ (void) slot;
+ (void) driver;
+
+#if defined(MBEDTLS_THREADING_C)
+ PSA_THREADING_CHK_RET(mbedtls_mutex_lock(
+ &mbedtls_threading_key_slot_mutex));
+#endif
+
+#if defined(MBEDTLS_PSA_CRYPTO_STORAGE_C)
+ if (!PSA_KEY_LIFETIME_IS_VOLATILE(slot->attr.lifetime)) {
+#if defined(MBEDTLS_PSA_CRYPTO_SE_C)
+ if (driver != NULL) {
+ psa_se_key_data_storage_t data;
+ psa_key_slot_number_t slot_number =
+ psa_key_slot_get_slot_number(slot);
+
+ MBEDTLS_STATIC_ASSERT(sizeof(slot_number) ==
+ sizeof(data.slot_number),
+ "Slot number size does not match psa_se_key_data_storage_t");
+
+ memcpy(&data.slot_number, &slot_number, sizeof(slot_number));
+ status = psa_save_persistent_key(&slot->attr,
+ (uint8_t *) &data,
+ sizeof(data));
+ } else
+#endif /* MBEDTLS_PSA_CRYPTO_SE_C */
+ {
+ /* Key material is saved in export representation in the slot, so
+ * just pass the slot buffer for storage. */
+ status = psa_save_persistent_key(&slot->attr,
+ slot->key.data,
+ slot->key.bytes);
+ }
+ }
+#endif /* defined(MBEDTLS_PSA_CRYPTO_STORAGE_C) */
+
+#if defined(MBEDTLS_PSA_CRYPTO_SE_C)
+ /* Finish the transaction for a key creation. This does not
+ * happen when registering an existing key. Detect this case
+ * by checking whether a transaction is in progress (actual
+ * creation of a persistent key in a secure element requires a transaction,
+ * but registration or volatile key creation doesn't use one). */
+ if (driver != NULL &&
+ psa_crypto_transaction.unknown.type == PSA_CRYPTO_TRANSACTION_CREATE_KEY) {
+ status = psa_save_se_persistent_data(driver);
+ if (status != PSA_SUCCESS) {
+ psa_destroy_persistent_key(slot->attr.id);
+
+#if defined(MBEDTLS_THREADING_C)
+ PSA_THREADING_CHK_RET(mbedtls_mutex_unlock(
+ &mbedtls_threading_key_slot_mutex));
+#endif
+ return status;
+ }
+ status = psa_crypto_stop_transaction();
+ }
+#endif /* MBEDTLS_PSA_CRYPTO_SE_C */
+
+ if (status == PSA_SUCCESS) {
+ *key = slot->attr.id;
+ status = psa_key_slot_state_transition(slot, PSA_SLOT_FILLING,
+ PSA_SLOT_FULL);
+ if (status != PSA_SUCCESS) {
+ *key = MBEDTLS_SVC_KEY_ID_INIT;
+ }
+ }
+
+#if defined(MBEDTLS_THREADING_C)
+ PSA_THREADING_CHK_RET(mbedtls_mutex_unlock(
+ &mbedtls_threading_key_slot_mutex));
+#endif
+ return status;
+}
+
+/** Abort the creation of a key.
+ *
+ * You may call this function after calling psa_start_key_creation(),
+ * or after psa_finish_key_creation() fails. In other circumstances, this
+ * function may not clean up persistent storage.
+ * See the documentation of psa_start_key_creation() for the intended use
+ * of this function. Sets the slot's state to PSA_SLOT_EMPTY.
+ *
+ * \param[in,out] slot Pointer to the slot with key material.
+ * \param[in] driver The secure element driver for the key,
+ * or NULL for a transparent key.
+ */
+static void psa_fail_key_creation(psa_key_slot_t *slot,
+ psa_se_drv_table_entry_t *driver)
+{
+ (void) driver;
+
+ if (slot == NULL) {
+ return;
+ }
+
+#if defined(MBEDTLS_THREADING_C)
+ /* If the lock operation fails we still wipe the slot.
+ * Operations will no longer work after a failed lock,
+ * but we still need to wipe the slot of confidential data. */
+ mbedtls_mutex_lock(&mbedtls_threading_key_slot_mutex);
+#endif
+
+#if defined(MBEDTLS_PSA_CRYPTO_SE_C)
+ /* TODO: If the key has already been created in the secure
+ * element, and the failure happened later (when saving metadata
+ * to internal storage), we need to destroy the key in the secure
+ * element.
+ * https://github.com/ARMmbed/mbed-crypto/issues/217
+ */
+
+ /* Abort the ongoing transaction if any (there may not be one if
+ * the creation process failed before starting one, or if the
+ * key creation is a registration of a key in a secure element).
+ * Earlier functions must already have done what it takes to undo any
+ * partial creation. All that's left is to update the transaction data
+ * itself. */
+ (void) psa_crypto_stop_transaction();
+#endif /* MBEDTLS_PSA_CRYPTO_SE_C */
+
+ psa_wipe_key_slot(slot);
+
+#if defined(MBEDTLS_THREADING_C)
+ mbedtls_mutex_unlock(&mbedtls_threading_key_slot_mutex);
+#endif
+}
+
+/** Validate optional attributes during key creation.
+ *
+ * Some key attributes are optional during key creation. If they are
+ * specified in the attributes structure, check that they are consistent
+ * with the data in the slot.
+ *
+ * This function should be called near the end of key creation, after
+ * the slot in memory is fully populated but before saving persistent data.
+ */
+static psa_status_t psa_validate_optional_attributes(
+ const psa_key_slot_t *slot,
+ const psa_key_attributes_t *attributes)
+{
+ if (attributes->type != 0) {
+ if (attributes->type != slot->attr.type) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+ }
+
+ if (attributes->bits != 0) {
+ if (attributes->bits != slot->attr.bits) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+ }
+
+ return PSA_SUCCESS;
+}
+
+psa_status_t psa_import_key(const psa_key_attributes_t *attributes,
+ const uint8_t *data_external,
+ size_t data_length,
+ mbedtls_svc_key_id_t *key)
+{
+ psa_status_t status;
+ LOCAL_INPUT_DECLARE(data_external, data);
+ psa_key_slot_t *slot = NULL;
+ psa_se_drv_table_entry_t *driver = NULL;
+ size_t bits;
+ size_t storage_size = data_length;
+
+ *key = MBEDTLS_SVC_KEY_ID_INIT;
+
+ /* Reject zero-length symmetric keys (including raw data key objects).
+ * This also rejects any key which might be encoded as an empty string,
+ * which is never valid. */
+ if (data_length == 0) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ /* Ensure that the bytes-to-bits conversion cannot overflow. */
+ if (data_length > SIZE_MAX / 8) {
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+
+ LOCAL_INPUT_ALLOC(data_external, data_length, data);
+
+ status = psa_start_key_creation(PSA_KEY_CREATION_IMPORT, attributes,
+ &slot, &driver);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ /* In the case of a transparent key or an opaque key stored in local
+ * storage ( thus not in the case of importing a key in a secure element
+ * with storage ( MBEDTLS_PSA_CRYPTO_SE_C ) ),we have to allocate a
+ * buffer to hold the imported key material. */
+ if (slot->key.data == NULL) {
+ if (psa_key_lifetime_is_external(attributes->lifetime)) {
+ status = psa_driver_wrapper_get_key_buffer_size_from_key_data(
+ attributes, data, data_length, &storage_size);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+ }
+ status = psa_allocate_buffer_to_slot(slot, storage_size);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+ }
+
+ bits = slot->attr.bits;
+ status = psa_driver_wrapper_import_key(attributes,
+ data, data_length,
+ slot->key.data,
+ slot->key.bytes,
+ &slot->key.bytes, &bits);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ if (slot->attr.bits == 0) {
+ slot->attr.bits = (psa_key_bits_t) bits;
+ } else if (bits != slot->attr.bits) {
+ status = PSA_ERROR_INVALID_ARGUMENT;
+ goto exit;
+ }
+
+ /* Enforce a size limit, and in particular ensure that the bit
+ * size fits in its representation type.*/
+ if (bits > PSA_MAX_KEY_BITS) {
+ status = PSA_ERROR_NOT_SUPPORTED;
+ goto exit;
+ }
+ status = psa_validate_optional_attributes(slot, attributes);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ status = psa_finish_key_creation(slot, driver, key);
+exit:
+ LOCAL_INPUT_FREE(data_external, data);
+ if (status != PSA_SUCCESS) {
+ psa_fail_key_creation(slot, driver);
+ }
+
+ return status;
+}
+
+#if defined(MBEDTLS_PSA_CRYPTO_SE_C)
+psa_status_t mbedtls_psa_register_se_key(
+ const psa_key_attributes_t *attributes)
+{
+ psa_status_t status;
+ psa_key_slot_t *slot = NULL;
+ psa_se_drv_table_entry_t *driver = NULL;
+ mbedtls_svc_key_id_t key = MBEDTLS_SVC_KEY_ID_INIT;
+
+ /* Leaving attributes unspecified is not currently supported.
+ * It could make sense to query the key type and size from the
+ * secure element, but not all secure elements support this
+ * and the driver HAL doesn't currently support it. */
+ if (psa_get_key_type(attributes) == PSA_KEY_TYPE_NONE) {
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+ if (psa_get_key_bits(attributes) == 0) {
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+
+ /* Not usable with volatile keys, even with an appropriate location,
+ * due to the API design.
+ * https://github.com/Mbed-TLS/mbedtls/issues/9253
+ */
+ if (PSA_KEY_LIFETIME_IS_VOLATILE(psa_get_key_lifetime(attributes))) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ status = psa_start_key_creation(PSA_KEY_CREATION_REGISTER, attributes,
+ &slot, &driver);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ status = psa_finish_key_creation(slot, driver, &key);
+
+exit:
+ if (status != PSA_SUCCESS) {
+ psa_fail_key_creation(slot, driver);
+ }
+
+ /* Registration doesn't keep the key in RAM. */
+ psa_close_key(key);
+ return status;
+}
+#endif /* MBEDTLS_PSA_CRYPTO_SE_C */
+
+psa_status_t psa_copy_key(mbedtls_svc_key_id_t source_key,
+ const psa_key_attributes_t *specified_attributes,
+ mbedtls_svc_key_id_t *target_key)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_key_slot_t *source_slot = NULL;
+ psa_key_slot_t *target_slot = NULL;
+ psa_key_attributes_t actual_attributes = *specified_attributes;
+ psa_se_drv_table_entry_t *driver = NULL;
+ size_t storage_size = 0;
+
+ *target_key = MBEDTLS_SVC_KEY_ID_INIT;
+
+ status = psa_get_and_lock_key_slot_with_policy(
+ source_key, &source_slot, PSA_KEY_USAGE_COPY, 0);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ status = psa_validate_optional_attributes(source_slot,
+ specified_attributes);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ /* The target key type and number of bits have been validated by
+ * psa_validate_optional_attributes() to be either equal to zero or
+ * equal to the ones of the source key. So it is safe to inherit
+ * them from the source key now."
+ * */
+ actual_attributes.bits = source_slot->attr.bits;
+ actual_attributes.type = source_slot->attr.type;
+
+
+ status = psa_restrict_key_policy(source_slot->attr.type,
+ &actual_attributes.policy,
+ &source_slot->attr.policy);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ status = psa_start_key_creation(PSA_KEY_CREATION_COPY, &actual_attributes,
+ &target_slot, &driver);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+ if (PSA_KEY_LIFETIME_GET_LOCATION(target_slot->attr.lifetime) !=
+ PSA_KEY_LIFETIME_GET_LOCATION(source_slot->attr.lifetime)) {
+ /*
+ * If the source and target keys are stored in different locations,
+ * the source key would need to be exported as plaintext and re-imported
+ * in the other location. This has security implications which have not
+ * been fully mapped. For now, this can be achieved through
+ * appropriate API invocations from the application, if needed.
+ * */
+ status = PSA_ERROR_NOT_SUPPORTED;
+ goto exit;
+ }
+ /*
+ * When the source and target keys are within the same location,
+ * - For transparent keys it is a blind copy without any driver invocation,
+ * - For opaque keys this translates to an invocation of the drivers'
+ * copy_key entry point through the dispatch layer.
+ * */
+ if (psa_key_lifetime_is_external(actual_attributes.lifetime)) {
+ status = psa_driver_wrapper_get_key_buffer_size(&actual_attributes,
+ &storage_size);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ status = psa_allocate_buffer_to_slot(target_slot, storage_size);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ status = psa_driver_wrapper_copy_key(&actual_attributes,
+ source_slot->key.data,
+ source_slot->key.bytes,
+ target_slot->key.data,
+ target_slot->key.bytes,
+ &target_slot->key.bytes);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+ } else {
+ status = psa_copy_key_material_into_slot(target_slot,
+ source_slot->key.data,
+ source_slot->key.bytes);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+ }
+ status = psa_finish_key_creation(target_slot, driver, target_key);
+exit:
+ if (status != PSA_SUCCESS) {
+ psa_fail_key_creation(target_slot, driver);
+ }
+
+ unlock_status = psa_unregister_read_under_mutex(source_slot);
+
+ return (status == PSA_SUCCESS) ? unlock_status : status;
+}
+
+
+
+/****************************************************************/
+/* Message digests */
+/****************************************************************/
+
+psa_status_t psa_hash_abort(psa_hash_operation_t *operation)
+{
+ /* Aborting a non-active operation is allowed */
+ if (operation->id == 0) {
+ return PSA_SUCCESS;
+ }
+
+ psa_status_t status = psa_driver_wrapper_hash_abort(operation);
+ operation->id = 0;
+
+ return status;
+}
+
+psa_status_t psa_hash_setup(psa_hash_operation_t *operation,
+ psa_algorithm_t alg)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+ /* A context must be freshly initialized before it can be set up. */
+ if (operation->id != 0) {
+ status = PSA_ERROR_BAD_STATE;
+ goto exit;
+ }
+
+ if (!PSA_ALG_IS_HASH(alg)) {
+ status = PSA_ERROR_INVALID_ARGUMENT;
+ goto exit;
+ }
+
+ /* Ensure all of the context is zeroized, since PSA_HASH_OPERATION_INIT only
+ * directly zeroes the int-sized dummy member of the context union. */
+ memset(&operation->ctx, 0, sizeof(operation->ctx));
+
+ status = psa_driver_wrapper_hash_setup(operation, alg);
+
+exit:
+ if (status != PSA_SUCCESS) {
+ psa_hash_abort(operation);
+ }
+
+ return status;
+}
+
+psa_status_t psa_hash_update(psa_hash_operation_t *operation,
+ const uint8_t *input_external,
+ size_t input_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ LOCAL_INPUT_DECLARE(input_external, input);
+
+ if (operation->id == 0) {
+ status = PSA_ERROR_BAD_STATE;
+ goto exit;
+ }
+
+ /* Don't require hash implementations to behave correctly on a
+ * zero-length input, which may have an invalid pointer. */
+ if (input_length == 0) {
+ return PSA_SUCCESS;
+ }
+
+ LOCAL_INPUT_ALLOC(input_external, input_length, input);
+ status = psa_driver_wrapper_hash_update(operation, input, input_length);
+
+exit:
+ if (status != PSA_SUCCESS) {
+ psa_hash_abort(operation);
+ }
+
+ LOCAL_INPUT_FREE(input_external, input);
+ return status;
+}
+
+static psa_status_t psa_hash_finish_internal(psa_hash_operation_t *operation,
+ uint8_t *hash,
+ size_t hash_size,
+ size_t *hash_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+ *hash_length = 0;
+ if (operation->id == 0) {
+ return PSA_ERROR_BAD_STATE;
+ }
+
+ status = psa_driver_wrapper_hash_finish(
+ operation, hash, hash_size, hash_length);
+ psa_hash_abort(operation);
+
+ return status;
+}
+
+psa_status_t psa_hash_finish(psa_hash_operation_t *operation,
+ uint8_t *hash_external,
+ size_t hash_size,
+ size_t *hash_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ LOCAL_OUTPUT_DECLARE(hash_external, hash);
+
+ LOCAL_OUTPUT_ALLOC(hash_external, hash_size, hash);
+ status = psa_hash_finish_internal(operation, hash, hash_size, hash_length);
+
+#if !defined(MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS)
+exit:
+#endif
+ LOCAL_OUTPUT_FREE(hash_external, hash);
+ return status;
+}
+
+psa_status_t psa_hash_verify(psa_hash_operation_t *operation,
+ const uint8_t *hash_external,
+ size_t hash_length)
+{
+ uint8_t actual_hash[PSA_HASH_MAX_SIZE];
+ size_t actual_hash_length;
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ LOCAL_INPUT_DECLARE(hash_external, hash);
+
+ status = psa_hash_finish_internal(
+ operation,
+ actual_hash, sizeof(actual_hash),
+ &actual_hash_length);
+
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ if (actual_hash_length != hash_length) {
+ status = PSA_ERROR_INVALID_SIGNATURE;
+ goto exit;
+ }
+
+ LOCAL_INPUT_ALLOC(hash_external, hash_length, hash);
+ if (mbedtls_ct_memcmp(hash, actual_hash, actual_hash_length) != 0) {
+ status = PSA_ERROR_INVALID_SIGNATURE;
+ }
+
+exit:
+ mbedtls_platform_zeroize(actual_hash, sizeof(actual_hash));
+ if (status != PSA_SUCCESS) {
+ psa_hash_abort(operation);
+ }
+ LOCAL_INPUT_FREE(hash_external, hash);
+ return status;
+}
+
+psa_status_t psa_hash_compute(psa_algorithm_t alg,
+ const uint8_t *input_external, size_t input_length,
+ uint8_t *hash_external, size_t hash_size,
+ size_t *hash_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ LOCAL_INPUT_DECLARE(input_external, input);
+ LOCAL_OUTPUT_DECLARE(hash_external, hash);
+
+ *hash_length = 0;
+ if (!PSA_ALG_IS_HASH(alg)) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ LOCAL_INPUT_ALLOC(input_external, input_length, input);
+ LOCAL_OUTPUT_ALLOC(hash_external, hash_size, hash);
+ status = psa_driver_wrapper_hash_compute(alg, input, input_length,
+ hash, hash_size, hash_length);
+
+#if !defined(MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS)
+exit:
+#endif
+ LOCAL_INPUT_FREE(input_external, input);
+ LOCAL_OUTPUT_FREE(hash_external, hash);
+ return status;
+}
+
+psa_status_t psa_hash_compare(psa_algorithm_t alg,
+ const uint8_t *input_external, size_t input_length,
+ const uint8_t *hash_external, size_t hash_length)
+{
+ uint8_t actual_hash[PSA_HASH_MAX_SIZE];
+ size_t actual_hash_length;
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+ LOCAL_INPUT_DECLARE(input_external, input);
+ LOCAL_INPUT_DECLARE(hash_external, hash);
+
+ if (!PSA_ALG_IS_HASH(alg)) {
+ status = PSA_ERROR_INVALID_ARGUMENT;
+ return status;
+ }
+
+ LOCAL_INPUT_ALLOC(input_external, input_length, input);
+ status = psa_driver_wrapper_hash_compute(
+ alg, input, input_length,
+ actual_hash, sizeof(actual_hash),
+ &actual_hash_length);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+ if (actual_hash_length != hash_length) {
+ status = PSA_ERROR_INVALID_SIGNATURE;
+ goto exit;
+ }
+
+ LOCAL_INPUT_ALLOC(hash_external, hash_length, hash);
+ if (mbedtls_ct_memcmp(hash, actual_hash, actual_hash_length) != 0) {
+ status = PSA_ERROR_INVALID_SIGNATURE;
+ }
+
+exit:
+ mbedtls_platform_zeroize(actual_hash, sizeof(actual_hash));
+
+ LOCAL_INPUT_FREE(input_external, input);
+ LOCAL_INPUT_FREE(hash_external, hash);
+
+ return status;
+}
+
+psa_status_t psa_hash_clone(const psa_hash_operation_t *source_operation,
+ psa_hash_operation_t *target_operation)
+{
+ if (source_operation->id == 0 ||
+ target_operation->id != 0) {
+ return PSA_ERROR_BAD_STATE;
+ }
+
+ psa_status_t status = psa_driver_wrapper_hash_clone(source_operation,
+ target_operation);
+ if (status != PSA_SUCCESS) {
+ psa_hash_abort(target_operation);
+ }
+
+ return status;
+}
+
+
+/****************************************************************/
+/* MAC */
+/****************************************************************/
+
+psa_status_t psa_mac_abort(psa_mac_operation_t *operation)
+{
+ /* Aborting a non-active operation is allowed */
+ if (operation->id == 0) {
+ return PSA_SUCCESS;
+ }
+
+ psa_status_t status = psa_driver_wrapper_mac_abort(operation);
+ operation->mac_size = 0;
+ operation->is_sign = 0;
+ operation->id = 0;
+
+ return status;
+}
+
+static psa_status_t psa_mac_finalize_alg_and_key_validation(
+ psa_algorithm_t alg,
+ const psa_key_attributes_t *attributes,
+ uint8_t *mac_size)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_key_type_t key_type = psa_get_key_type(attributes);
+ size_t key_bits = psa_get_key_bits(attributes);
+
+ if (!PSA_ALG_IS_MAC(alg)) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ /* Validate the combination of key type and algorithm */
+ status = psa_mac_key_can_do(alg, key_type);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ /* Get the output length for the algorithm and key combination */
+ *mac_size = PSA_MAC_LENGTH(key_type, key_bits, alg);
+
+ if (*mac_size < 4) {
+ /* A very short MAC is too short for security since it can be
+ * brute-forced. Ancient protocols with 32-bit MACs do exist,
+ * so we make this our minimum, even though 32 bits is still
+ * too small for security. */
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+
+ if (*mac_size > PSA_MAC_LENGTH(key_type, key_bits,
+ PSA_ALG_FULL_LENGTH_MAC(alg))) {
+ /* It's impossible to "truncate" to a larger length than the full length
+ * of the algorithm. */
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ if (*mac_size > PSA_MAC_MAX_SIZE) {
+ /* PSA_MAC_LENGTH returns the correct length even for a MAC algorithm
+ * that is disabled in the compile-time configuration. The result can
+ * therefore be larger than PSA_MAC_MAX_SIZE, which does take the
+ * configuration into account. In this case, force a return of
+ * PSA_ERROR_NOT_SUPPORTED here. Otherwise psa_mac_verify(), or
+ * psa_mac_compute(mac_size=PSA_MAC_MAX_SIZE), would return
+ * PSA_ERROR_BUFFER_TOO_SMALL for an unsupported algorithm whose MAC size
+ * is larger than PSA_MAC_MAX_SIZE, which is misleading and which breaks
+ * systematically generated tests. */
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+
+ return PSA_SUCCESS;
+}
+
+static psa_status_t psa_mac_setup(psa_mac_operation_t *operation,
+ mbedtls_svc_key_id_t key,
+ psa_algorithm_t alg,
+ int is_sign)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_key_slot_t *slot = NULL;
+
+ /* A context must be freshly initialized before it can be set up. */
+ if (operation->id != 0) {
+ status = PSA_ERROR_BAD_STATE;
+ goto exit;
+ }
+
+ status = psa_get_and_lock_key_slot_with_policy(
+ key,
+ &slot,
+ is_sign ? PSA_KEY_USAGE_SIGN_MESSAGE : PSA_KEY_USAGE_VERIFY_MESSAGE,
+ alg);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ status = psa_mac_finalize_alg_and_key_validation(alg, &slot->attr,
+ &operation->mac_size);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ operation->is_sign = is_sign;
+ /* Dispatch the MAC setup call with validated input */
+ if (is_sign) {
+ status = psa_driver_wrapper_mac_sign_setup(operation,
+ &slot->attr,
+ slot->key.data,
+ slot->key.bytes,
+ alg);
+ } else {
+ status = psa_driver_wrapper_mac_verify_setup(operation,
+ &slot->attr,
+ slot->key.data,
+ slot->key.bytes,
+ alg);
+ }
+
+exit:
+ if (status != PSA_SUCCESS) {
+ psa_mac_abort(operation);
+ }
+
+ unlock_status = psa_unregister_read_under_mutex(slot);
+
+ return (status == PSA_SUCCESS) ? unlock_status : status;
+}
+
+psa_status_t psa_mac_sign_setup(psa_mac_operation_t *operation,
+ mbedtls_svc_key_id_t key,
+ psa_algorithm_t alg)
+{
+ return psa_mac_setup(operation, key, alg, 1);
+}
+
+psa_status_t psa_mac_verify_setup(psa_mac_operation_t *operation,
+ mbedtls_svc_key_id_t key,
+ psa_algorithm_t alg)
+{
+ return psa_mac_setup(operation, key, alg, 0);
+}
+
+psa_status_t psa_mac_update(psa_mac_operation_t *operation,
+ const uint8_t *input_external,
+ size_t input_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ LOCAL_INPUT_DECLARE(input_external, input);
+
+ if (operation->id == 0) {
+ status = PSA_ERROR_BAD_STATE;
+ return status;
+ }
+
+ /* Don't require hash implementations to behave correctly on a
+ * zero-length input, which may have an invalid pointer. */
+ if (input_length == 0) {
+ status = PSA_SUCCESS;
+ return status;
+ }
+
+ LOCAL_INPUT_ALLOC(input_external, input_length, input);
+ status = psa_driver_wrapper_mac_update(operation, input, input_length);
+
+ if (status != PSA_SUCCESS) {
+ psa_mac_abort(operation);
+ }
+
+#if !defined(MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS)
+exit:
+#endif
+ LOCAL_INPUT_FREE(input_external, input);
+
+ return status;
+}
+
+psa_status_t psa_mac_sign_finish(psa_mac_operation_t *operation,
+ uint8_t *mac_external,
+ size_t mac_size,
+ size_t *mac_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_status_t abort_status = PSA_ERROR_CORRUPTION_DETECTED;
+ LOCAL_OUTPUT_DECLARE(mac_external, mac);
+ LOCAL_OUTPUT_ALLOC(mac_external, mac_size, mac);
+
+ if (operation->id == 0) {
+ status = PSA_ERROR_BAD_STATE;
+ goto exit;
+ }
+
+ if (!operation->is_sign) {
+ status = PSA_ERROR_BAD_STATE;
+ goto exit;
+ }
+
+ /* Sanity check. This will guarantee that mac_size != 0 (and so mac != NULL)
+ * once all the error checks are done. */
+ if (operation->mac_size == 0) {
+ status = PSA_ERROR_BAD_STATE;
+ goto exit;
+ }
+
+ if (mac_size < operation->mac_size) {
+ status = PSA_ERROR_BUFFER_TOO_SMALL;
+ goto exit;
+ }
+
+
+ status = psa_driver_wrapper_mac_sign_finish(operation,
+ mac, operation->mac_size,
+ mac_length);
+
+exit:
+ /* In case of success, set the potential excess room in the output buffer
+ * to an invalid value, to avoid potentially leaking a longer MAC.
+ * In case of error, set the output length and content to a safe default,
+ * such that in case the caller misses an error check, the output would be
+ * an unachievable MAC.
+ */
+ if (status != PSA_SUCCESS) {
+ *mac_length = mac_size;
+ operation->mac_size = 0;
+ }
+
+ if (mac != NULL) {
+ psa_wipe_tag_output_buffer(mac, status, mac_size, *mac_length);
+ }
+
+ abort_status = psa_mac_abort(operation);
+ LOCAL_OUTPUT_FREE(mac_external, mac);
+
+ return status == PSA_SUCCESS ? abort_status : status;
+}
+
+psa_status_t psa_mac_verify_finish(psa_mac_operation_t *operation,
+ const uint8_t *mac_external,
+ size_t mac_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_status_t abort_status = PSA_ERROR_CORRUPTION_DETECTED;
+ LOCAL_INPUT_DECLARE(mac_external, mac);
+
+ if (operation->id == 0) {
+ status = PSA_ERROR_BAD_STATE;
+ goto exit;
+ }
+
+ if (operation->is_sign) {
+ status = PSA_ERROR_BAD_STATE;
+ goto exit;
+ }
+
+ if (operation->mac_size != mac_length) {
+ status = PSA_ERROR_INVALID_SIGNATURE;
+ goto exit;
+ }
+
+ LOCAL_INPUT_ALLOC(mac_external, mac_length, mac);
+ status = psa_driver_wrapper_mac_verify_finish(operation,
+ mac, mac_length);
+
+exit:
+ abort_status = psa_mac_abort(operation);
+ LOCAL_INPUT_FREE(mac_external, mac);
+
+ return status == PSA_SUCCESS ? abort_status : status;
+}
+
+static psa_status_t psa_mac_compute_internal(mbedtls_svc_key_id_t key,
+ psa_algorithm_t alg,
+ const uint8_t *input,
+ size_t input_length,
+ uint8_t *mac,
+ size_t mac_size,
+ size_t *mac_length,
+ int is_sign)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_key_slot_t *slot;
+ uint8_t operation_mac_size = 0;
+
+ status = psa_get_and_lock_key_slot_with_policy(
+ key,
+ &slot,
+ is_sign ? PSA_KEY_USAGE_SIGN_MESSAGE : PSA_KEY_USAGE_VERIFY_MESSAGE,
+ alg);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ status = psa_mac_finalize_alg_and_key_validation(alg, &slot->attr,
+ &operation_mac_size);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ if (mac_size < operation_mac_size) {
+ status = PSA_ERROR_BUFFER_TOO_SMALL;
+ goto exit;
+ }
+
+ status = psa_driver_wrapper_mac_compute(
+ &slot->attr,
+ slot->key.data, slot->key.bytes,
+ alg,
+ input, input_length,
+ mac, operation_mac_size, mac_length);
+
+exit:
+ /* In case of success, set the potential excess room in the output buffer
+ * to an invalid value, to avoid potentially leaking a longer MAC.
+ * In case of error, set the output length and content to a safe default,
+ * such that in case the caller misses an error check, the output would be
+ * an unachievable MAC.
+ */
+ if (status != PSA_SUCCESS) {
+ *mac_length = mac_size;
+ operation_mac_size = 0;
+ }
+
+ psa_wipe_tag_output_buffer(mac, status, mac_size, *mac_length);
+
+ unlock_status = psa_unregister_read_under_mutex(slot);
+
+ return (status == PSA_SUCCESS) ? unlock_status : status;
+}
+
+psa_status_t psa_mac_compute(mbedtls_svc_key_id_t key,
+ psa_algorithm_t alg,
+ const uint8_t *input_external,
+ size_t input_length,
+ uint8_t *mac_external,
+ size_t mac_size,
+ size_t *mac_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ LOCAL_INPUT_DECLARE(input_external, input);
+ LOCAL_OUTPUT_DECLARE(mac_external, mac);
+
+ LOCAL_INPUT_ALLOC(input_external, input_length, input);
+ LOCAL_OUTPUT_ALLOC(mac_external, mac_size, mac);
+ status = psa_mac_compute_internal(key, alg,
+ input, input_length,
+ mac, mac_size, mac_length, 1);
+
+#if !defined(MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS)
+exit:
+#endif
+ LOCAL_INPUT_FREE(input_external, input);
+ LOCAL_OUTPUT_FREE(mac_external, mac);
+
+ return status;
+}
+
+psa_status_t psa_mac_verify(mbedtls_svc_key_id_t key,
+ psa_algorithm_t alg,
+ const uint8_t *input_external,
+ size_t input_length,
+ const uint8_t *mac_external,
+ size_t mac_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ uint8_t actual_mac[PSA_MAC_MAX_SIZE];
+ size_t actual_mac_length;
+ LOCAL_INPUT_DECLARE(input_external, input);
+ LOCAL_INPUT_DECLARE(mac_external, mac);
+
+ LOCAL_INPUT_ALLOC(input_external, input_length, input);
+ status = psa_mac_compute_internal(key, alg,
+ input, input_length,
+ actual_mac, sizeof(actual_mac),
+ &actual_mac_length, 0);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ if (mac_length != actual_mac_length) {
+ status = PSA_ERROR_INVALID_SIGNATURE;
+ goto exit;
+ }
+
+ LOCAL_INPUT_ALLOC(mac_external, mac_length, mac);
+ if (mbedtls_ct_memcmp(mac, actual_mac, actual_mac_length) != 0) {
+ status = PSA_ERROR_INVALID_SIGNATURE;
+ goto exit;
+ }
+
+exit:
+ mbedtls_platform_zeroize(actual_mac, sizeof(actual_mac));
+ LOCAL_INPUT_FREE(input_external, input);
+ LOCAL_INPUT_FREE(mac_external, mac);
+
+ return status;
+}
+
+/****************************************************************/
+/* Asymmetric cryptography */
+/****************************************************************/
+
+static psa_status_t psa_sign_verify_check_alg(int input_is_message,
+ psa_algorithm_t alg)
+{
+ if (input_is_message) {
+ if (!PSA_ALG_IS_SIGN_MESSAGE(alg)) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ if (PSA_ALG_IS_SIGN_HASH(alg)) {
+ if (!PSA_ALG_IS_HASH(PSA_ALG_SIGN_GET_HASH(alg))) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+ }
+ } else {
+ if (!PSA_ALG_IS_SIGN_HASH(alg)) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+ }
+
+ return PSA_SUCCESS;
+}
+
+static psa_status_t psa_sign_internal(mbedtls_svc_key_id_t key,
+ int input_is_message,
+ psa_algorithm_t alg,
+ const uint8_t *input,
+ size_t input_length,
+ uint8_t *signature,
+ size_t signature_size,
+ size_t *signature_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_key_slot_t *slot;
+
+ *signature_length = 0;
+
+ status = psa_sign_verify_check_alg(input_is_message, alg);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ /* Immediately reject a zero-length signature buffer. This guarantees
+ * that signature must be a valid pointer. (On the other hand, the input
+ * buffer can in principle be empty since it doesn't actually have
+ * to be a hash.) */
+ if (signature_size == 0) {
+ return PSA_ERROR_BUFFER_TOO_SMALL;
+ }
+
+ status = psa_get_and_lock_key_slot_with_policy(
+ key, &slot,
+ input_is_message ? PSA_KEY_USAGE_SIGN_MESSAGE :
+ PSA_KEY_USAGE_SIGN_HASH,
+ alg);
+
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ if (!PSA_KEY_TYPE_IS_KEY_PAIR(slot->attr.type)) {
+ status = PSA_ERROR_INVALID_ARGUMENT;
+ goto exit;
+ }
+
+ if (input_is_message) {
+ status = psa_driver_wrapper_sign_message(
+ &slot->attr, slot->key.data, slot->key.bytes,
+ alg, input, input_length,
+ signature, signature_size, signature_length);
+ } else {
+
+ status = psa_driver_wrapper_sign_hash(
+ &slot->attr, slot->key.data, slot->key.bytes,
+ alg, input, input_length,
+ signature, signature_size, signature_length);
+ }
+
+
+exit:
+ psa_wipe_tag_output_buffer(signature, status, signature_size,
+ *signature_length);
+
+ unlock_status = psa_unregister_read_under_mutex(slot);
+
+ return (status == PSA_SUCCESS) ? unlock_status : status;
+}
+
+static psa_status_t psa_verify_internal(mbedtls_svc_key_id_t key,
+ int input_is_message,
+ psa_algorithm_t alg,
+ const uint8_t *input,
+ size_t input_length,
+ const uint8_t *signature,
+ size_t signature_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_key_slot_t *slot;
+
+ status = psa_sign_verify_check_alg(input_is_message, alg);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ status = psa_get_and_lock_key_slot_with_policy(
+ key, &slot,
+ input_is_message ? PSA_KEY_USAGE_VERIFY_MESSAGE :
+ PSA_KEY_USAGE_VERIFY_HASH,
+ alg);
+
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ if (input_is_message) {
+ status = psa_driver_wrapper_verify_message(
+ &slot->attr, slot->key.data, slot->key.bytes,
+ alg, input, input_length,
+ signature, signature_length);
+ } else {
+ status = psa_driver_wrapper_verify_hash(
+ &slot->attr, slot->key.data, slot->key.bytes,
+ alg, input, input_length,
+ signature, signature_length);
+ }
+
+ unlock_status = psa_unregister_read_under_mutex(slot);
+
+ return (status == PSA_SUCCESS) ? unlock_status : status;
+
+}
+
+psa_status_t psa_sign_message_builtin(
+ const psa_key_attributes_t *attributes,
+ const uint8_t *key_buffer,
+ size_t key_buffer_size,
+ psa_algorithm_t alg,
+ const uint8_t *input,
+ size_t input_length,
+ uint8_t *signature,
+ size_t signature_size,
+ size_t *signature_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+ if (PSA_ALG_IS_SIGN_HASH(alg)) {
+ size_t hash_length;
+ uint8_t hash[PSA_HASH_MAX_SIZE];
+
+ status = psa_driver_wrapper_hash_compute(
+ PSA_ALG_SIGN_GET_HASH(alg),
+ input, input_length,
+ hash, sizeof(hash), &hash_length);
+
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ return psa_driver_wrapper_sign_hash(
+ attributes, key_buffer, key_buffer_size,
+ alg, hash, hash_length,
+ signature, signature_size, signature_length);
+ }
+
+ return PSA_ERROR_NOT_SUPPORTED;
+}
+
+psa_status_t psa_sign_message(mbedtls_svc_key_id_t key,
+ psa_algorithm_t alg,
+ const uint8_t *input_external,
+ size_t input_length,
+ uint8_t *signature_external,
+ size_t signature_size,
+ size_t *signature_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ LOCAL_INPUT_DECLARE(input_external, input);
+ LOCAL_OUTPUT_DECLARE(signature_external, signature);
+
+ LOCAL_INPUT_ALLOC(input_external, input_length, input);
+ LOCAL_OUTPUT_ALLOC(signature_external, signature_size, signature);
+ status = psa_sign_internal(key, 1, alg, input, input_length, signature,
+ signature_size, signature_length);
+
+#if !defined(MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS)
+exit:
+#endif
+ LOCAL_INPUT_FREE(input_external, input);
+ LOCAL_OUTPUT_FREE(signature_external, signature);
+ return status;
+}
+
+psa_status_t psa_verify_message_builtin(
+ const psa_key_attributes_t *attributes,
+ const uint8_t *key_buffer,
+ size_t key_buffer_size,
+ psa_algorithm_t alg,
+ const uint8_t *input,
+ size_t input_length,
+ const uint8_t *signature,
+ size_t signature_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+ if (PSA_ALG_IS_SIGN_HASH(alg)) {
+ size_t hash_length;
+ uint8_t hash[PSA_HASH_MAX_SIZE];
+
+ status = psa_driver_wrapper_hash_compute(
+ PSA_ALG_SIGN_GET_HASH(alg),
+ input, input_length,
+ hash, sizeof(hash), &hash_length);
+
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ return psa_driver_wrapper_verify_hash(
+ attributes, key_buffer, key_buffer_size,
+ alg, hash, hash_length,
+ signature, signature_length);
+ }
+
+ return PSA_ERROR_NOT_SUPPORTED;
+}
+
+psa_status_t psa_verify_message(mbedtls_svc_key_id_t key,
+ psa_algorithm_t alg,
+ const uint8_t *input_external,
+ size_t input_length,
+ const uint8_t *signature_external,
+ size_t signature_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ LOCAL_INPUT_DECLARE(input_external, input);
+ LOCAL_INPUT_DECLARE(signature_external, signature);
+
+ LOCAL_INPUT_ALLOC(input_external, input_length, input);
+ LOCAL_INPUT_ALLOC(signature_external, signature_length, signature);
+ status = psa_verify_internal(key, 1, alg, input, input_length, signature,
+ signature_length);
+
+#if !defined(MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS)
+exit:
+#endif
+ LOCAL_INPUT_FREE(input_external, input);
+ LOCAL_INPUT_FREE(signature_external, signature);
+
+ return status;
+}
+
+psa_status_t psa_sign_hash_builtin(
+ const psa_key_attributes_t *attributes,
+ const uint8_t *key_buffer, size_t key_buffer_size,
+ psa_algorithm_t alg, const uint8_t *hash, size_t hash_length,
+ uint8_t *signature, size_t signature_size, size_t *signature_length)
+{
+ if (attributes->type == PSA_KEY_TYPE_RSA_KEY_PAIR) {
+ if (PSA_ALG_IS_RSA_PKCS1V15_SIGN(alg) ||
+ PSA_ALG_IS_RSA_PSS(alg)) {
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_SIGN) || \
+ defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PSS)
+ return mbedtls_psa_rsa_sign_hash(
+ attributes,
+ key_buffer, key_buffer_size,
+ alg, hash, hash_length,
+ signature, signature_size, signature_length);
+#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_SIGN) ||
+ * defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PSS) */
+ } else {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+ } else if (PSA_KEY_TYPE_IS_ECC(attributes->type)) {
+ if (PSA_ALG_IS_ECDSA(alg)) {
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || \
+ defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA)
+ return mbedtls_psa_ecdsa_sign_hash(
+ attributes,
+ key_buffer, key_buffer_size,
+ alg, hash, hash_length,
+ signature, signature_size, signature_length);
+#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) ||
+ * defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) */
+ } else {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+ }
+
+ (void) key_buffer;
+ (void) key_buffer_size;
+ (void) hash;
+ (void) hash_length;
+ (void) signature;
+ (void) signature_size;
+ (void) signature_length;
+
+ return PSA_ERROR_NOT_SUPPORTED;
+}
+
+psa_status_t psa_sign_hash(mbedtls_svc_key_id_t key,
+ psa_algorithm_t alg,
+ const uint8_t *hash_external,
+ size_t hash_length,
+ uint8_t *signature_external,
+ size_t signature_size,
+ size_t *signature_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ LOCAL_INPUT_DECLARE(hash_external, hash);
+ LOCAL_OUTPUT_DECLARE(signature_external, signature);
+
+ LOCAL_INPUT_ALLOC(hash_external, hash_length, hash);
+ LOCAL_OUTPUT_ALLOC(signature_external, signature_size, signature);
+ status = psa_sign_internal(key, 0, alg, hash, hash_length, signature,
+ signature_size, signature_length);
+
+#if !defined(MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS)
+exit:
+#endif
+ LOCAL_INPUT_FREE(hash_external, hash);
+ LOCAL_OUTPUT_FREE(signature_external, signature);
+
+ return status;
+}
+
+psa_status_t psa_verify_hash_builtin(
+ const psa_key_attributes_t *attributes,
+ const uint8_t *key_buffer, size_t key_buffer_size,
+ psa_algorithm_t alg, const uint8_t *hash, size_t hash_length,
+ const uint8_t *signature, size_t signature_length)
+{
+ if (PSA_KEY_TYPE_IS_RSA(attributes->type)) {
+ if (PSA_ALG_IS_RSA_PKCS1V15_SIGN(alg) ||
+ PSA_ALG_IS_RSA_PSS(alg)) {
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_SIGN) || \
+ defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PSS)
+ return mbedtls_psa_rsa_verify_hash(
+ attributes,
+ key_buffer, key_buffer_size,
+ alg, hash, hash_length,
+ signature, signature_length);
+#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_SIGN) ||
+ * defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PSS) */
+ } else {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+ } else if (PSA_KEY_TYPE_IS_ECC(attributes->type)) {
+ if (PSA_ALG_IS_ECDSA(alg)) {
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || \
+ defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA)
+ return mbedtls_psa_ecdsa_verify_hash(
+ attributes,
+ key_buffer, key_buffer_size,
+ alg, hash, hash_length,
+ signature, signature_length);
+#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) ||
+ * defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) */
+ } else {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+ }
+
+ (void) key_buffer;
+ (void) key_buffer_size;
+ (void) hash;
+ (void) hash_length;
+ (void) signature;
+ (void) signature_length;
+
+ return PSA_ERROR_NOT_SUPPORTED;
+}
+
+psa_status_t psa_verify_hash(mbedtls_svc_key_id_t key,
+ psa_algorithm_t alg,
+ const uint8_t *hash_external,
+ size_t hash_length,
+ const uint8_t *signature_external,
+ size_t signature_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ LOCAL_INPUT_DECLARE(hash_external, hash);
+ LOCAL_INPUT_DECLARE(signature_external, signature);
+
+ LOCAL_INPUT_ALLOC(hash_external, hash_length, hash);
+ LOCAL_INPUT_ALLOC(signature_external, signature_length, signature);
+ status = psa_verify_internal(key, 0, alg, hash, hash_length, signature,
+ signature_length);
+
+#if !defined(MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS)
+exit:
+#endif
+ LOCAL_INPUT_FREE(hash_external, hash);
+ LOCAL_INPUT_FREE(signature_external, signature);
+
+ return status;
+}
+
+psa_status_t psa_asymmetric_encrypt(mbedtls_svc_key_id_t key,
+ psa_algorithm_t alg,
+ const uint8_t *input_external,
+ size_t input_length,
+ const uint8_t *salt_external,
+ size_t salt_length,
+ uint8_t *output_external,
+ size_t output_size,
+ size_t *output_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_key_slot_t *slot;
+
+ LOCAL_INPUT_DECLARE(input_external, input);
+ LOCAL_INPUT_DECLARE(salt_external, salt);
+ LOCAL_OUTPUT_DECLARE(output_external, output);
+
+ (void) input;
+ (void) input_length;
+ (void) salt;
+ (void) output;
+ (void) output_size;
+
+ *output_length = 0;
+
+ if (!PSA_ALG_IS_RSA_OAEP(alg) && salt_length != 0) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ status = psa_get_and_lock_key_slot_with_policy(
+ key, &slot, PSA_KEY_USAGE_ENCRYPT, alg);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+ if (!(PSA_KEY_TYPE_IS_PUBLIC_KEY(slot->attr.type) ||
+ PSA_KEY_TYPE_IS_KEY_PAIR(slot->attr.type))) {
+ status = PSA_ERROR_INVALID_ARGUMENT;
+ goto exit;
+ }
+
+ LOCAL_INPUT_ALLOC(input_external, input_length, input);
+ LOCAL_INPUT_ALLOC(salt_external, salt_length, salt);
+ LOCAL_OUTPUT_ALLOC(output_external, output_size, output);
+
+ status = psa_driver_wrapper_asymmetric_encrypt(
+ &slot->attr, slot->key.data, slot->key.bytes,
+ alg, input, input_length, salt, salt_length,
+ output, output_size, output_length);
+exit:
+ unlock_status = psa_unregister_read_under_mutex(slot);
+
+ LOCAL_INPUT_FREE(input_external, input);
+ LOCAL_INPUT_FREE(salt_external, salt);
+ LOCAL_OUTPUT_FREE(output_external, output);
+
+ return (status == PSA_SUCCESS) ? unlock_status : status;
+}
+
+psa_status_t psa_asymmetric_decrypt(mbedtls_svc_key_id_t key,
+ psa_algorithm_t alg,
+ const uint8_t *input_external,
+ size_t input_length,
+ const uint8_t *salt_external,
+ size_t salt_length,
+ uint8_t *output_external,
+ size_t output_size,
+ size_t *output_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_key_slot_t *slot;
+
+ LOCAL_INPUT_DECLARE(input_external, input);
+ LOCAL_INPUT_DECLARE(salt_external, salt);
+ LOCAL_OUTPUT_DECLARE(output_external, output);
+
+ (void) input;
+ (void) input_length;
+ (void) salt;
+ (void) output;
+ (void) output_size;
+
+ *output_length = 0;
+
+ if (!PSA_ALG_IS_RSA_OAEP(alg) && salt_length != 0) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ status = psa_get_and_lock_key_slot_with_policy(
+ key, &slot, PSA_KEY_USAGE_DECRYPT, alg);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+ if (!PSA_KEY_TYPE_IS_KEY_PAIR(slot->attr.type)) {
+ status = PSA_ERROR_INVALID_ARGUMENT;
+ goto exit;
+ }
+
+ LOCAL_INPUT_ALLOC(input_external, input_length, input);
+ LOCAL_INPUT_ALLOC(salt_external, salt_length, salt);
+ LOCAL_OUTPUT_ALLOC(output_external, output_size, output);
+
+ status = psa_driver_wrapper_asymmetric_decrypt(
+ &slot->attr, slot->key.data, slot->key.bytes,
+ alg, input, input_length, salt, salt_length,
+ output, output_size, output_length);
+
+exit:
+ unlock_status = psa_unregister_read_under_mutex(slot);
+
+ LOCAL_INPUT_FREE(input_external, input);
+ LOCAL_INPUT_FREE(salt_external, salt);
+ LOCAL_OUTPUT_FREE(output_external, output);
+
+ return (status == PSA_SUCCESS) ? unlock_status : status;
+}
+
+/****************************************************************/
+/* Asymmetric interruptible cryptography */
+/****************************************************************/
+
+static uint32_t psa_interruptible_max_ops = PSA_INTERRUPTIBLE_MAX_OPS_UNLIMITED;
+
+void psa_interruptible_set_max_ops(uint32_t max_ops)
+{
+ psa_interruptible_max_ops = max_ops;
+}
+
+uint32_t psa_interruptible_get_max_ops(void)
+{
+ return psa_interruptible_max_ops;
+}
+
+uint32_t psa_sign_hash_get_num_ops(
+ const psa_sign_hash_interruptible_operation_t *operation)
+{
+ return operation->num_ops;
+}
+
+uint32_t psa_verify_hash_get_num_ops(
+ const psa_verify_hash_interruptible_operation_t *operation)
+{
+ return operation->num_ops;
+}
+
+static psa_status_t psa_sign_hash_abort_internal(
+ psa_sign_hash_interruptible_operation_t *operation)
+{
+ if (operation->id == 0) {
+ /* The object has (apparently) been initialized but it is not (yet)
+ * in use. It's ok to call abort on such an object, and there's
+ * nothing to do. */
+ return PSA_SUCCESS;
+ }
+
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+ status = psa_driver_wrapper_sign_hash_abort(operation);
+
+ operation->id = 0;
+
+ /* Do not clear either the error_occurred or num_ops elements here as they
+ * only want to be cleared by the application calling abort, not by abort
+ * being called at completion of an operation. */
+
+ return status;
+}
+
+psa_status_t psa_sign_hash_start(
+ psa_sign_hash_interruptible_operation_t *operation,
+ mbedtls_svc_key_id_t key, psa_algorithm_t alg,
+ const uint8_t *hash_external, size_t hash_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_key_slot_t *slot;
+
+ LOCAL_INPUT_DECLARE(hash_external, hash);
+
+ /* Check that start has not been previously called, or operation has not
+ * previously errored. */
+ if (operation->id != 0 || operation->error_occurred) {
+ return PSA_ERROR_BAD_STATE;
+ }
+
+ status = psa_sign_verify_check_alg(0, alg);
+ if (status != PSA_SUCCESS) {
+ operation->error_occurred = 1;
+ return status;
+ }
+
+ status = psa_get_and_lock_key_slot_with_policy(key, &slot,
+ PSA_KEY_USAGE_SIGN_HASH,
+ alg);
+
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ if (!PSA_KEY_TYPE_IS_KEY_PAIR(slot->attr.type)) {
+ status = PSA_ERROR_INVALID_ARGUMENT;
+ goto exit;
+ }
+
+ LOCAL_INPUT_ALLOC(hash_external, hash_length, hash);
+
+ /* Ensure ops count gets reset, in case of operation re-use. */
+ operation->num_ops = 0;
+
+ status = psa_driver_wrapper_sign_hash_start(operation, &slot->attr,
+ slot->key.data,
+ slot->key.bytes, alg,
+ hash, hash_length);
+exit:
+
+ if (status != PSA_SUCCESS) {
+ operation->error_occurred = 1;
+ psa_sign_hash_abort_internal(operation);
+ }
+
+ unlock_status = psa_unregister_read_under_mutex(slot);
+
+ if (unlock_status != PSA_SUCCESS) {
+ operation->error_occurred = 1;
+ }
+
+ LOCAL_INPUT_FREE(hash_external, hash);
+
+ return (status == PSA_SUCCESS) ? unlock_status : status;
+}
+
+
+psa_status_t psa_sign_hash_complete(
+ psa_sign_hash_interruptible_operation_t *operation,
+ uint8_t *signature_external, size_t signature_size,
+ size_t *signature_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+ LOCAL_OUTPUT_DECLARE(signature_external, signature);
+
+ *signature_length = 0;
+
+ /* Check that start has been called first, and that operation has not
+ * previously errored. */
+ if (operation->id == 0 || operation->error_occurred) {
+ status = PSA_ERROR_BAD_STATE;
+ goto exit;
+ }
+
+ /* Immediately reject a zero-length signature buffer. This guarantees that
+ * signature must be a valid pointer. */
+ if (signature_size == 0) {
+ status = PSA_ERROR_BUFFER_TOO_SMALL;
+ goto exit;
+ }
+
+ LOCAL_OUTPUT_ALLOC(signature_external, signature_size, signature);
+
+ status = psa_driver_wrapper_sign_hash_complete(operation, signature,
+ signature_size,
+ signature_length);
+
+ /* Update ops count with work done. */
+ operation->num_ops = psa_driver_wrapper_sign_hash_get_num_ops(operation);
+
+exit:
+
+ if (signature != NULL) {
+ psa_wipe_tag_output_buffer(signature, status, signature_size,
+ *signature_length);
+ }
+
+ if (status != PSA_OPERATION_INCOMPLETE) {
+ if (status != PSA_SUCCESS) {
+ operation->error_occurred = 1;
+ }
+
+ psa_sign_hash_abort_internal(operation);
+ }
+
+ LOCAL_OUTPUT_FREE(signature_external, signature);
+
+ return status;
+}
+
+psa_status_t psa_sign_hash_abort(
+ psa_sign_hash_interruptible_operation_t *operation)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+ status = psa_sign_hash_abort_internal(operation);
+
+ /* We clear the number of ops done here, so that it is not cleared when
+ * the operation fails or succeeds, only on manual abort. */
+ operation->num_ops = 0;
+
+ /* Likewise, failure state. */
+ operation->error_occurred = 0;
+
+ return status;
+}
+
+static psa_status_t psa_verify_hash_abort_internal(
+ psa_verify_hash_interruptible_operation_t *operation)
+{
+ if (operation->id == 0) {
+ /* The object has (apparently) been initialized but it is not (yet)
+ * in use. It's ok to call abort on such an object, and there's
+ * nothing to do. */
+ return PSA_SUCCESS;
+ }
+
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+ status = psa_driver_wrapper_verify_hash_abort(operation);
+
+ operation->id = 0;
+
+ /* Do not clear either the error_occurred or num_ops elements here as they
+ * only want to be cleared by the application calling abort, not by abort
+ * being called at completion of an operation. */
+
+ return status;
+}
+
+psa_status_t psa_verify_hash_start(
+ psa_verify_hash_interruptible_operation_t *operation,
+ mbedtls_svc_key_id_t key, psa_algorithm_t alg,
+ const uint8_t *hash_external, size_t hash_length,
+ const uint8_t *signature_external, size_t signature_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_key_slot_t *slot;
+
+ LOCAL_INPUT_DECLARE(hash_external, hash);
+ LOCAL_INPUT_DECLARE(signature_external, signature);
+
+ /* Check that start has not been previously called, or operation has not
+ * previously errored. */
+ if (operation->id != 0 || operation->error_occurred) {
+ return PSA_ERROR_BAD_STATE;
+ }
+
+ status = psa_sign_verify_check_alg(0, alg);
+ if (status != PSA_SUCCESS) {
+ operation->error_occurred = 1;
+ return status;
+ }
+
+ status = psa_get_and_lock_key_slot_with_policy(key, &slot,
+ PSA_KEY_USAGE_VERIFY_HASH,
+ alg);
+
+ if (status != PSA_SUCCESS) {
+ operation->error_occurred = 1;
+ return status;
+ }
+
+ LOCAL_INPUT_ALLOC(hash_external, hash_length, hash);
+ LOCAL_INPUT_ALLOC(signature_external, signature_length, signature);
+
+ /* Ensure ops count gets reset, in case of operation re-use. */
+ operation->num_ops = 0;
+
+ status = psa_driver_wrapper_verify_hash_start(operation, &slot->attr,
+ slot->key.data,
+ slot->key.bytes,
+ alg, hash, hash_length,
+ signature, signature_length);
+#if !defined(MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS)
+exit:
+#endif
+
+ if (status != PSA_SUCCESS) {
+ operation->error_occurred = 1;
+ psa_verify_hash_abort_internal(operation);
+ }
+
+ unlock_status = psa_unregister_read_under_mutex(slot);
+
+ if (unlock_status != PSA_SUCCESS) {
+ operation->error_occurred = 1;
+ }
+
+ LOCAL_INPUT_FREE(hash_external, hash);
+ LOCAL_INPUT_FREE(signature_external, signature);
+
+ return (status == PSA_SUCCESS) ? unlock_status : status;
+}
+
+psa_status_t psa_verify_hash_complete(
+ psa_verify_hash_interruptible_operation_t *operation)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+ /* Check that start has been called first, and that operation has not
+ * previously errored. */
+ if (operation->id == 0 || operation->error_occurred) {
+ status = PSA_ERROR_BAD_STATE;
+ goto exit;
+ }
+
+ status = psa_driver_wrapper_verify_hash_complete(operation);
+
+ /* Update ops count with work done. */
+ operation->num_ops = psa_driver_wrapper_verify_hash_get_num_ops(
+ operation);
+
+exit:
+
+ if (status != PSA_OPERATION_INCOMPLETE) {
+ if (status != PSA_SUCCESS) {
+ operation->error_occurred = 1;
+ }
+
+ psa_verify_hash_abort_internal(operation);
+ }
+
+ return status;
+}
+
+psa_status_t psa_verify_hash_abort(
+ psa_verify_hash_interruptible_operation_t *operation)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+ status = psa_verify_hash_abort_internal(operation);
+
+ /* We clear the number of ops done here, so that it is not cleared when
+ * the operation fails or succeeds, only on manual abort. */
+ operation->num_ops = 0;
+
+ /* Likewise, failure state. */
+ operation->error_occurred = 0;
+
+ return status;
+}
+
+/****************************************************************/
+/* Asymmetric interruptible cryptography internal */
+/* implementations */
+/****************************************************************/
+
+void mbedtls_psa_interruptible_set_max_ops(uint32_t max_ops)
+{
+
+#if (defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || \
+ defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA)) && \
+ defined(MBEDTLS_ECP_RESTARTABLE)
+
+ /* Internal implementation uses zero to indicate infinite number max ops,
+ * therefore avoid this value, and set to minimum possible. */
+ if (max_ops == 0) {
+ max_ops = 1;
+ }
+
+ mbedtls_ecp_set_max_ops(max_ops);
+#else
+ (void) max_ops;
+#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) ||
+ * defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) &&
+ * defined( MBEDTLS_ECP_RESTARTABLE ) */
+}
+
+uint32_t mbedtls_psa_sign_hash_get_num_ops(
+ const mbedtls_psa_sign_hash_interruptible_operation_t *operation)
+{
+#if (defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || \
+ defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA)) && \
+ defined(MBEDTLS_ECP_RESTARTABLE)
+
+ return operation->num_ops;
+#else
+ (void) operation;
+ return 0;
+#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) ||
+ * defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) &&
+ * defined( MBEDTLS_ECP_RESTARTABLE ) */
+}
+
+uint32_t mbedtls_psa_verify_hash_get_num_ops(
+ const mbedtls_psa_verify_hash_interruptible_operation_t *operation)
+{
+ #if (defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || \
+ defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA)) && \
+ defined(MBEDTLS_ECP_RESTARTABLE)
+
+ return operation->num_ops;
+#else
+ (void) operation;
+ return 0;
+#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) ||
+ * defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) &&
+ * defined( MBEDTLS_ECP_RESTARTABLE ) */
+}
+
+psa_status_t mbedtls_psa_sign_hash_start(
+ mbedtls_psa_sign_hash_interruptible_operation_t *operation,
+ const psa_key_attributes_t *attributes, const uint8_t *key_buffer,
+ size_t key_buffer_size, psa_algorithm_t alg,
+ const uint8_t *hash, size_t hash_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ size_t required_hash_length;
+
+ if (!PSA_KEY_TYPE_IS_ECC(attributes->type)) {
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+
+ if (!PSA_ALG_IS_ECDSA(alg)) {
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+
+#if (defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || \
+ defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA)) && \
+ defined(MBEDTLS_ECP_RESTARTABLE)
+
+ mbedtls_ecdsa_restart_init(&operation->restart_ctx);
+
+ /* Ensure num_ops is zero'ed in case of context re-use. */
+ operation->num_ops = 0;
+
+ status = mbedtls_psa_ecp_load_representation(attributes->type,
+ attributes->bits,
+ key_buffer,
+ key_buffer_size,
+ &operation->ctx);
+
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ operation->coordinate_bytes = PSA_BITS_TO_BYTES(
+ operation->ctx->grp.nbits);
+
+ psa_algorithm_t hash_alg = PSA_ALG_SIGN_GET_HASH(alg);
+ operation->md_alg = mbedtls_md_type_from_psa_alg(hash_alg);
+ operation->alg = alg;
+
+ /* We only need to store the same length of hash as the private key size
+ * here, it would be truncated by the internal implementation anyway. */
+ required_hash_length = (hash_length < operation->coordinate_bytes ?
+ hash_length : operation->coordinate_bytes);
+
+ if (required_hash_length > sizeof(operation->hash)) {
+ /* Shouldn't happen, but better safe than sorry. */
+ return PSA_ERROR_CORRUPTION_DETECTED;
+ }
+
+ memcpy(operation->hash, hash, required_hash_length);
+ operation->hash_length = required_hash_length;
+
+ return PSA_SUCCESS;
+
+#else
+ (void) operation;
+ (void) key_buffer;
+ (void) key_buffer_size;
+ (void) alg;
+ (void) hash;
+ (void) hash_length;
+ (void) status;
+ (void) required_hash_length;
+
+ return PSA_ERROR_NOT_SUPPORTED;
+#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) ||
+ * defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) &&
+ * defined( MBEDTLS_ECP_RESTARTABLE ) */
+}
+
+psa_status_t mbedtls_psa_sign_hash_complete(
+ mbedtls_psa_sign_hash_interruptible_operation_t *operation,
+ uint8_t *signature, size_t signature_size,
+ size_t *signature_length)
+{
+#if (defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || \
+ defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA)) && \
+ defined(MBEDTLS_ECP_RESTARTABLE)
+
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ mbedtls_mpi r;
+ mbedtls_mpi s;
+
+ mbedtls_mpi_init(&r);
+ mbedtls_mpi_init(&s);
+
+ /* Ensure max_ops is set to the current value (or default). */
+ mbedtls_psa_interruptible_set_max_ops(psa_interruptible_get_max_ops());
+
+ if (signature_size < 2 * operation->coordinate_bytes) {
+ status = PSA_ERROR_BUFFER_TOO_SMALL;
+ goto exit;
+ }
+
+ if (PSA_ALG_ECDSA_IS_DETERMINISTIC(operation->alg)) {
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA)
+ status = mbedtls_to_psa_error(
+ mbedtls_ecdsa_sign_det_restartable(&operation->ctx->grp,
+ &r,
+ &s,
+ &operation->ctx->d,
+ operation->hash,
+ operation->hash_length,
+ operation->md_alg,
+ mbedtls_psa_get_random,
+ MBEDTLS_PSA_RANDOM_STATE,
+ &operation->restart_ctx));
+#else /* defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) */
+ status = PSA_ERROR_NOT_SUPPORTED;
+ goto exit;
+#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) */
+ } else {
+ status = mbedtls_to_psa_error(
+ mbedtls_ecdsa_sign_restartable(&operation->ctx->grp,
+ &r,
+ &s,
+ &operation->ctx->d,
+ operation->hash,
+ operation->hash_length,
+ mbedtls_psa_get_random,
+ MBEDTLS_PSA_RANDOM_STATE,
+ mbedtls_psa_get_random,
+ MBEDTLS_PSA_RANDOM_STATE,
+ &operation->restart_ctx));
+ }
+
+ /* Hide the fact that the restart context only holds a delta of number of
+ * ops done during the last operation, not an absolute value. */
+ operation->num_ops += operation->restart_ctx.ecp.ops_done;
+
+ if (status == PSA_SUCCESS) {
+ status = mbedtls_to_psa_error(
+ mbedtls_mpi_write_binary(&r,
+ signature,
+ operation->coordinate_bytes)
+ );
+
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ status = mbedtls_to_psa_error(
+ mbedtls_mpi_write_binary(&s,
+ signature +
+ operation->coordinate_bytes,
+ operation->coordinate_bytes)
+ );
+
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ *signature_length = operation->coordinate_bytes * 2;
+
+ status = PSA_SUCCESS;
+ }
+
+exit:
+
+ mbedtls_mpi_free(&r);
+ mbedtls_mpi_free(&s);
+ return status;
+
+ #else
+
+ (void) operation;
+ (void) signature;
+ (void) signature_size;
+ (void) signature_length;
+
+ return PSA_ERROR_NOT_SUPPORTED;
+
+#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) ||
+ * defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) &&
+ * defined( MBEDTLS_ECP_RESTARTABLE ) */
+}
+
+psa_status_t mbedtls_psa_sign_hash_abort(
+ mbedtls_psa_sign_hash_interruptible_operation_t *operation)
+{
+
+#if (defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || \
+ defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA)) && \
+ defined(MBEDTLS_ECP_RESTARTABLE)
+
+ if (operation->ctx) {
+ mbedtls_ecdsa_free(operation->ctx);
+ mbedtls_free(operation->ctx);
+ operation->ctx = NULL;
+ }
+
+ mbedtls_ecdsa_restart_free(&operation->restart_ctx);
+
+ operation->num_ops = 0;
+
+ return PSA_SUCCESS;
+
+#else
+
+ (void) operation;
+
+ return PSA_ERROR_NOT_SUPPORTED;
+
+#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) ||
+ * defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) &&
+ * defined( MBEDTLS_ECP_RESTARTABLE ) */
+}
+
+psa_status_t mbedtls_psa_verify_hash_start(
+ mbedtls_psa_verify_hash_interruptible_operation_t *operation,
+ const psa_key_attributes_t *attributes,
+ const uint8_t *key_buffer, size_t key_buffer_size,
+ psa_algorithm_t alg,
+ const uint8_t *hash, size_t hash_length,
+ const uint8_t *signature, size_t signature_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ size_t coordinate_bytes = 0;
+ size_t required_hash_length = 0;
+
+ if (!PSA_KEY_TYPE_IS_ECC(attributes->type)) {
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+
+ if (!PSA_ALG_IS_ECDSA(alg)) {
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+
+#if (defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || \
+ defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA)) && \
+ defined(MBEDTLS_ECP_RESTARTABLE)
+
+ mbedtls_ecdsa_restart_init(&operation->restart_ctx);
+ mbedtls_mpi_init(&operation->r);
+ mbedtls_mpi_init(&operation->s);
+
+ /* Ensure num_ops is zero'ed in case of context re-use. */
+ operation->num_ops = 0;
+
+ status = mbedtls_psa_ecp_load_representation(attributes->type,
+ attributes->bits,
+ key_buffer,
+ key_buffer_size,
+ &operation->ctx);
+
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ coordinate_bytes = PSA_BITS_TO_BYTES(operation->ctx->grp.nbits);
+
+ if (signature_length != 2 * coordinate_bytes) {
+ return PSA_ERROR_INVALID_SIGNATURE;
+ }
+
+ status = mbedtls_to_psa_error(
+ mbedtls_mpi_read_binary(&operation->r,
+ signature,
+ coordinate_bytes));
+
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ status = mbedtls_to_psa_error(
+ mbedtls_mpi_read_binary(&operation->s,
+ signature +
+ coordinate_bytes,
+ coordinate_bytes));
+
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ status = mbedtls_psa_ecp_load_public_part(operation->ctx);
+
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ /* We only need to store the same length of hash as the private key size
+ * here, it would be truncated by the internal implementation anyway. */
+ required_hash_length = (hash_length < coordinate_bytes ? hash_length :
+ coordinate_bytes);
+
+ if (required_hash_length > sizeof(operation->hash)) {
+ /* Shouldn't happen, but better safe than sorry. */
+ return PSA_ERROR_CORRUPTION_DETECTED;
+ }
+
+ memcpy(operation->hash, hash, required_hash_length);
+ operation->hash_length = required_hash_length;
+
+ return PSA_SUCCESS;
+#else
+ (void) operation;
+ (void) key_buffer;
+ (void) key_buffer_size;
+ (void) alg;
+ (void) hash;
+ (void) hash_length;
+ (void) signature;
+ (void) signature_length;
+ (void) status;
+ (void) coordinate_bytes;
+ (void) required_hash_length;
+
+ return PSA_ERROR_NOT_SUPPORTED;
+#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) ||
+ * defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) &&
+ * defined( MBEDTLS_ECP_RESTARTABLE ) */
+}
+
+psa_status_t mbedtls_psa_verify_hash_complete(
+ mbedtls_psa_verify_hash_interruptible_operation_t *operation)
+{
+
+#if (defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || \
+ defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA)) && \
+ defined(MBEDTLS_ECP_RESTARTABLE)
+
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+ /* Ensure max_ops is set to the current value (or default). */
+ mbedtls_psa_interruptible_set_max_ops(psa_interruptible_get_max_ops());
+
+ status = mbedtls_to_psa_error(
+ mbedtls_ecdsa_verify_restartable(&operation->ctx->grp,
+ operation->hash,
+ operation->hash_length,
+ &operation->ctx->Q,
+ &operation->r,
+ &operation->s,
+ &operation->restart_ctx));
+
+ /* Hide the fact that the restart context only holds a delta of number of
+ * ops done during the last operation, not an absolute value. */
+ operation->num_ops += operation->restart_ctx.ecp.ops_done;
+
+ return status;
+#else
+ (void) operation;
+
+ return PSA_ERROR_NOT_SUPPORTED;
+
+#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) ||
+ * defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) &&
+ * defined( MBEDTLS_ECP_RESTARTABLE ) */
+}
+
+psa_status_t mbedtls_psa_verify_hash_abort(
+ mbedtls_psa_verify_hash_interruptible_operation_t *operation)
+{
+
+#if (defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || \
+ defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA)) && \
+ defined(MBEDTLS_ECP_RESTARTABLE)
+
+ if (operation->ctx) {
+ mbedtls_ecdsa_free(operation->ctx);
+ mbedtls_free(operation->ctx);
+ operation->ctx = NULL;
+ }
+
+ mbedtls_ecdsa_restart_free(&operation->restart_ctx);
+
+ operation->num_ops = 0;
+
+ mbedtls_mpi_free(&operation->r);
+ mbedtls_mpi_free(&operation->s);
+
+ return PSA_SUCCESS;
+
+#else
+ (void) operation;
+
+ return PSA_ERROR_NOT_SUPPORTED;
+
+#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) ||
+ * defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) &&
+ * defined( MBEDTLS_ECP_RESTARTABLE ) */
+}
+
+static psa_status_t psa_generate_random_internal(uint8_t *output,
+ size_t output_size)
+{
+ GUARD_MODULE_INITIALIZED;
+
+#if defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG)
+
+ psa_status_t status;
+ size_t output_length = 0;
+ status = mbedtls_psa_external_get_random(&global_data.rng,
+ output, output_size,
+ &output_length);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+ /* Breaking up a request into smaller chunks is currently not supported
+ * for the external RNG interface. */
+ if (output_length != output_size) {
+ return PSA_ERROR_INSUFFICIENT_ENTROPY;
+ }
+ return PSA_SUCCESS;
+
+#else /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */
+
+ while (output_size > 0) {
+ int ret = MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED;
+ size_t request_size =
+ (output_size > MBEDTLS_PSA_RANDOM_MAX_REQUEST ?
+ MBEDTLS_PSA_RANDOM_MAX_REQUEST :
+ output_size);
+#if defined(MBEDTLS_CTR_DRBG_C)
+ ret = mbedtls_ctr_drbg_random(&global_data.rng.drbg, output, request_size);
+#elif defined(MBEDTLS_HMAC_DRBG_C)
+ ret = mbedtls_hmac_drbg_random(&global_data.rng.drbg, output, request_size);
+#endif /* !MBEDTLS_CTR_DRBG_C && !MBEDTLS_HMAC_DRBG_C */
+ if (ret != 0) {
+ return mbedtls_to_psa_error(ret);
+ }
+ output_size -= request_size;
+ output += request_size;
+ }
+ return PSA_SUCCESS;
+#endif /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */
+}
+
+
+/****************************************************************/
+/* Symmetric cryptography */
+/****************************************************************/
+
+static psa_status_t psa_cipher_setup(psa_cipher_operation_t *operation,
+ mbedtls_svc_key_id_t key,
+ psa_algorithm_t alg,
+ mbedtls_operation_t cipher_operation)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_key_slot_t *slot = NULL;
+ psa_key_usage_t usage = (cipher_operation == MBEDTLS_ENCRYPT ?
+ PSA_KEY_USAGE_ENCRYPT :
+ PSA_KEY_USAGE_DECRYPT);
+
+ /* A context must be freshly initialized before it can be set up. */
+ if (operation->id != 0) {
+ status = PSA_ERROR_BAD_STATE;
+ goto exit;
+ }
+
+ if (!PSA_ALG_IS_CIPHER(alg)) {
+ status = PSA_ERROR_INVALID_ARGUMENT;
+ goto exit;
+ }
+
+ status = psa_get_and_lock_key_slot_with_policy(key, &slot, usage, alg);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ /* Initialize the operation struct members, except for id. The id member
+ * is used to indicate to psa_cipher_abort that there are resources to free,
+ * so we only set it (in the driver wrapper) after resources have been
+ * allocated/initialized. */
+ operation->iv_set = 0;
+ if (alg == PSA_ALG_ECB_NO_PADDING) {
+ operation->iv_required = 0;
+ } else {
+ operation->iv_required = 1;
+ }
+ operation->default_iv_length = PSA_CIPHER_IV_LENGTH(slot->attr.type, alg);
+
+ /* Try doing the operation through a driver before using software fallback. */
+ if (cipher_operation == MBEDTLS_ENCRYPT) {
+ status = psa_driver_wrapper_cipher_encrypt_setup(operation,
+ &slot->attr,
+ slot->key.data,
+ slot->key.bytes,
+ alg);
+ } else {
+ status = psa_driver_wrapper_cipher_decrypt_setup(operation,
+ &slot->attr,
+ slot->key.data,
+ slot->key.bytes,
+ alg);
+ }
+
+exit:
+ if (status != PSA_SUCCESS) {
+ psa_cipher_abort(operation);
+ }
+
+ unlock_status = psa_unregister_read_under_mutex(slot);
+
+ return (status == PSA_SUCCESS) ? unlock_status : status;
+}
+
+psa_status_t psa_cipher_encrypt_setup(psa_cipher_operation_t *operation,
+ mbedtls_svc_key_id_t key,
+ psa_algorithm_t alg)
+{
+ return psa_cipher_setup(operation, key, alg, MBEDTLS_ENCRYPT);
+}
+
+psa_status_t psa_cipher_decrypt_setup(psa_cipher_operation_t *operation,
+ mbedtls_svc_key_id_t key,
+ psa_algorithm_t alg)
+{
+ return psa_cipher_setup(operation, key, alg, MBEDTLS_DECRYPT);
+}
+
+psa_status_t psa_cipher_generate_iv(psa_cipher_operation_t *operation,
+ uint8_t *iv_external,
+ size_t iv_size,
+ size_t *iv_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ size_t default_iv_length = 0;
+
+ LOCAL_OUTPUT_DECLARE(iv_external, iv);
+
+ if (operation->id == 0) {
+ status = PSA_ERROR_BAD_STATE;
+ goto exit;
+ }
+
+ if (operation->iv_set || !operation->iv_required) {
+ status = PSA_ERROR_BAD_STATE;
+ goto exit;
+ }
+
+ default_iv_length = operation->default_iv_length;
+ if (iv_size < default_iv_length) {
+ status = PSA_ERROR_BUFFER_TOO_SMALL;
+ goto exit;
+ }
+
+ if (default_iv_length > PSA_CIPHER_IV_MAX_SIZE) {
+ status = PSA_ERROR_GENERIC_ERROR;
+ goto exit;
+ }
+
+ LOCAL_OUTPUT_ALLOC(iv_external, default_iv_length, iv);
+
+ status = psa_generate_random_internal(iv, default_iv_length);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ status = psa_driver_wrapper_cipher_set_iv(operation,
+ iv, default_iv_length);
+
+exit:
+ if (status == PSA_SUCCESS) {
+ *iv_length = default_iv_length;
+ operation->iv_set = 1;
+ } else {
+ *iv_length = 0;
+ psa_cipher_abort(operation);
+ if (iv != NULL) {
+ mbedtls_platform_zeroize(iv, default_iv_length);
+ }
+ }
+
+ LOCAL_OUTPUT_FREE(iv_external, iv);
+ return status;
+}
+
+psa_status_t psa_cipher_set_iv(psa_cipher_operation_t *operation,
+ const uint8_t *iv_external,
+ size_t iv_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+ LOCAL_INPUT_DECLARE(iv_external, iv);
+
+ if (operation->id == 0) {
+ status = PSA_ERROR_BAD_STATE;
+ goto exit;
+ }
+
+ if (operation->iv_set || !operation->iv_required) {
+ status = PSA_ERROR_BAD_STATE;
+ goto exit;
+ }
+
+ if (iv_length > PSA_CIPHER_IV_MAX_SIZE) {
+ status = PSA_ERROR_INVALID_ARGUMENT;
+ goto exit;
+ }
+
+ LOCAL_INPUT_ALLOC(iv_external, iv_length, iv);
+
+ status = psa_driver_wrapper_cipher_set_iv(operation,
+ iv,
+ iv_length);
+
+exit:
+ if (status == PSA_SUCCESS) {
+ operation->iv_set = 1;
+ } else {
+ psa_cipher_abort(operation);
+ }
+
+ LOCAL_INPUT_FREE(iv_external, iv);
+
+ return status;
+}
+
+psa_status_t psa_cipher_update(psa_cipher_operation_t *operation,
+ const uint8_t *input_external,
+ size_t input_length,
+ uint8_t *output_external,
+ size_t output_size,
+ size_t *output_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+ LOCAL_INPUT_DECLARE(input_external, input);
+ LOCAL_OUTPUT_DECLARE(output_external, output);
+
+ if (operation->id == 0) {
+ status = PSA_ERROR_BAD_STATE;
+ goto exit;
+ }
+
+ if (operation->iv_required && !operation->iv_set) {
+ status = PSA_ERROR_BAD_STATE;
+ goto exit;
+ }
+
+ LOCAL_INPUT_ALLOC(input_external, input_length, input);
+ LOCAL_OUTPUT_ALLOC(output_external, output_size, output);
+
+ status = psa_driver_wrapper_cipher_update(operation,
+ input,
+ input_length,
+ output,
+ output_size,
+ output_length);
+
+exit:
+ if (status != PSA_SUCCESS) {
+ psa_cipher_abort(operation);
+ }
+
+ LOCAL_INPUT_FREE(input_external, input);
+ LOCAL_OUTPUT_FREE(output_external, output);
+
+ return status;
+}
+
+psa_status_t psa_cipher_finish(psa_cipher_operation_t *operation,
+ uint8_t *output_external,
+ size_t output_size,
+ size_t *output_length)
+{
+ psa_status_t status = PSA_ERROR_GENERIC_ERROR;
+
+ LOCAL_OUTPUT_DECLARE(output_external, output);
+
+ if (operation->id == 0) {
+ status = PSA_ERROR_BAD_STATE;
+ goto exit;
+ }
+
+ if (operation->iv_required && !operation->iv_set) {
+ status = PSA_ERROR_BAD_STATE;
+ goto exit;
+ }
+
+ LOCAL_OUTPUT_ALLOC(output_external, output_size, output);
+
+ status = psa_driver_wrapper_cipher_finish(operation,
+ output,
+ output_size,
+ output_length);
+
+exit:
+ if (status == PSA_SUCCESS) {
+ status = psa_cipher_abort(operation);
+ } else {
+ *output_length = 0;
+ (void) psa_cipher_abort(operation);
+ }
+
+ LOCAL_OUTPUT_FREE(output_external, output);
+
+ return status;
+}
+
+psa_status_t psa_cipher_abort(psa_cipher_operation_t *operation)
+{
+ if (operation->id == 0) {
+ /* The object has (apparently) been initialized but it is not (yet)
+ * in use. It's ok to call abort on such an object, and there's
+ * nothing to do. */
+ return PSA_SUCCESS;
+ }
+
+ psa_driver_wrapper_cipher_abort(operation);
+
+ operation->id = 0;
+ operation->iv_set = 0;
+ operation->iv_required = 0;
+
+ return PSA_SUCCESS;
+}
+
+psa_status_t psa_cipher_encrypt(mbedtls_svc_key_id_t key,
+ psa_algorithm_t alg,
+ const uint8_t *input_external,
+ size_t input_length,
+ uint8_t *output_external,
+ size_t output_size,
+ size_t *output_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_key_slot_t *slot = NULL;
+ uint8_t local_iv[PSA_CIPHER_IV_MAX_SIZE];
+ size_t default_iv_length = 0;
+
+ LOCAL_INPUT_DECLARE(input_external, input);
+ LOCAL_OUTPUT_DECLARE(output_external, output);
+
+ if (!PSA_ALG_IS_CIPHER(alg)) {
+ status = PSA_ERROR_INVALID_ARGUMENT;
+ goto exit;
+ }
+
+ status = psa_get_and_lock_key_slot_with_policy(key, &slot,
+ PSA_KEY_USAGE_ENCRYPT,
+ alg);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ default_iv_length = PSA_CIPHER_IV_LENGTH(slot->attr.type, alg);
+ if (default_iv_length > PSA_CIPHER_IV_MAX_SIZE) {
+ status = PSA_ERROR_GENERIC_ERROR;
+ goto exit;
+ }
+
+ if (default_iv_length > 0) {
+ if (output_size < default_iv_length) {
+ status = PSA_ERROR_BUFFER_TOO_SMALL;
+ goto exit;
+ }
+
+ status = psa_generate_random_internal(local_iv, default_iv_length);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+ }
+
+ LOCAL_INPUT_ALLOC(input_external, input_length, input);
+ LOCAL_OUTPUT_ALLOC(output_external, output_size, output);
+
+ status = psa_driver_wrapper_cipher_encrypt(
+ &slot->attr, slot->key.data, slot->key.bytes,
+ alg, local_iv, default_iv_length, input, input_length,
+ psa_crypto_buffer_offset(output, default_iv_length),
+ output_size - default_iv_length, output_length);
+
+exit:
+ unlock_status = psa_unregister_read_under_mutex(slot);
+ if (status == PSA_SUCCESS) {
+ status = unlock_status;
+ }
+
+ if (status == PSA_SUCCESS) {
+ if (default_iv_length > 0) {
+ memcpy(output, local_iv, default_iv_length);
+ }
+ *output_length += default_iv_length;
+ } else {
+ *output_length = 0;
+ }
+
+ LOCAL_INPUT_FREE(input_external, input);
+ LOCAL_OUTPUT_FREE(output_external, output);
+
+ return status;
+}
+
+psa_status_t psa_cipher_decrypt(mbedtls_svc_key_id_t key,
+ psa_algorithm_t alg,
+ const uint8_t *input_external,
+ size_t input_length,
+ uint8_t *output_external,
+ size_t output_size,
+ size_t *output_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_key_slot_t *slot = NULL;
+
+ LOCAL_INPUT_DECLARE(input_external, input);
+ LOCAL_OUTPUT_DECLARE(output_external, output);
+
+ if (!PSA_ALG_IS_CIPHER(alg)) {
+ status = PSA_ERROR_INVALID_ARGUMENT;
+ goto exit;
+ }
+
+ status = psa_get_and_lock_key_slot_with_policy(key, &slot,
+ PSA_KEY_USAGE_DECRYPT,
+ alg);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ if (input_length < PSA_CIPHER_IV_LENGTH(slot->attr.type, alg)) {
+ status = PSA_ERROR_INVALID_ARGUMENT;
+ goto exit;
+ }
+
+ LOCAL_INPUT_ALLOC(input_external, input_length, input);
+ LOCAL_OUTPUT_ALLOC(output_external, output_size, output);
+
+ status = psa_driver_wrapper_cipher_decrypt(
+ &slot->attr, slot->key.data, slot->key.bytes,
+ alg, input, input_length,
+ output, output_size, output_length);
+
+exit:
+ unlock_status = psa_unregister_read_under_mutex(slot);
+ if (status == PSA_SUCCESS) {
+ status = unlock_status;
+ }
+
+ if (status != PSA_SUCCESS) {
+ *output_length = 0;
+ }
+
+ LOCAL_INPUT_FREE(input_external, input);
+ LOCAL_OUTPUT_FREE(output_external, output);
+
+ return status;
+}
+
+
+/****************************************************************/
+/* AEAD */
+/****************************************************************/
+
+/* Helper function to get the base algorithm from its variants. */
+static psa_algorithm_t psa_aead_get_base_algorithm(psa_algorithm_t alg)
+{
+ return PSA_ALG_AEAD_WITH_DEFAULT_LENGTH_TAG(alg);
+}
+
+/* Helper function to perform common nonce length checks. */
+static psa_status_t psa_aead_check_nonce_length(psa_algorithm_t alg,
+ size_t nonce_length)
+{
+ psa_algorithm_t base_alg = psa_aead_get_base_algorithm(alg);
+
+ switch (base_alg) {
+#if defined(PSA_WANT_ALG_GCM)
+ case PSA_ALG_GCM:
+ /* Not checking max nonce size here as GCM spec allows almost
+ * arbitrarily large nonces. Please note that we do not generally
+ * recommend the usage of nonces of greater length than
+ * PSA_AEAD_NONCE_MAX_SIZE, as large nonces are hashed to a shorter
+ * size, which can then lead to collisions if you encrypt a very
+ * large number of messages.*/
+ if (nonce_length != 0) {
+ return PSA_SUCCESS;
+ }
+ break;
+#endif /* PSA_WANT_ALG_GCM */
+#if defined(PSA_WANT_ALG_CCM)
+ case PSA_ALG_CCM:
+ if (nonce_length >= 7 && nonce_length <= 13) {
+ return PSA_SUCCESS;
+ }
+ break;
+#endif /* PSA_WANT_ALG_CCM */
+#if defined(PSA_WANT_ALG_CHACHA20_POLY1305)
+ case PSA_ALG_CHACHA20_POLY1305:
+ if (nonce_length == 12) {
+ return PSA_SUCCESS;
+ } else if (nonce_length == 8) {
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+ break;
+#endif /* PSA_WANT_ALG_CHACHA20_POLY1305 */
+ default:
+ (void) nonce_length;
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+
+ return PSA_ERROR_INVALID_ARGUMENT;
+}
+
+static psa_status_t psa_aead_check_algorithm(psa_algorithm_t alg)
+{
+ if (!PSA_ALG_IS_AEAD(alg) || PSA_ALG_IS_WILDCARD(alg)) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ return PSA_SUCCESS;
+}
+
+psa_status_t psa_aead_encrypt(mbedtls_svc_key_id_t key,
+ psa_algorithm_t alg,
+ const uint8_t *nonce_external,
+ size_t nonce_length,
+ const uint8_t *additional_data_external,
+ size_t additional_data_length,
+ const uint8_t *plaintext_external,
+ size_t plaintext_length,
+ uint8_t *ciphertext_external,
+ size_t ciphertext_size,
+ size_t *ciphertext_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_key_slot_t *slot;
+
+ LOCAL_INPUT_DECLARE(nonce_external, nonce);
+ LOCAL_INPUT_DECLARE(additional_data_external, additional_data);
+ LOCAL_INPUT_DECLARE(plaintext_external, plaintext);
+ LOCAL_OUTPUT_DECLARE(ciphertext_external, ciphertext);
+
+ *ciphertext_length = 0;
+
+ status = psa_aead_check_algorithm(alg);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ status = psa_get_and_lock_key_slot_with_policy(
+ key, &slot, PSA_KEY_USAGE_ENCRYPT, alg);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ LOCAL_INPUT_ALLOC(nonce_external, nonce_length, nonce);
+ LOCAL_INPUT_ALLOC(additional_data_external, additional_data_length, additional_data);
+ LOCAL_INPUT_ALLOC(plaintext_external, plaintext_length, plaintext);
+ LOCAL_OUTPUT_ALLOC(ciphertext_external, ciphertext_size, ciphertext);
+
+ status = psa_aead_check_nonce_length(alg, nonce_length);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ status = psa_driver_wrapper_aead_encrypt(
+ &slot->attr, slot->key.data, slot->key.bytes,
+ alg,
+ nonce, nonce_length,
+ additional_data, additional_data_length,
+ plaintext, plaintext_length,
+ ciphertext, ciphertext_size, ciphertext_length);
+
+ if (status != PSA_SUCCESS && ciphertext_size != 0) {
+ memset(ciphertext, 0, ciphertext_size);
+ }
+
+exit:
+ LOCAL_INPUT_FREE(nonce_external, nonce);
+ LOCAL_INPUT_FREE(additional_data_external, additional_data);
+ LOCAL_INPUT_FREE(plaintext_external, plaintext);
+ LOCAL_OUTPUT_FREE(ciphertext_external, ciphertext);
+
+ psa_unregister_read_under_mutex(slot);
+
+ return status;
+}
+
+psa_status_t psa_aead_decrypt(mbedtls_svc_key_id_t key,
+ psa_algorithm_t alg,
+ const uint8_t *nonce_external,
+ size_t nonce_length,
+ const uint8_t *additional_data_external,
+ size_t additional_data_length,
+ const uint8_t *ciphertext_external,
+ size_t ciphertext_length,
+ uint8_t *plaintext_external,
+ size_t plaintext_size,
+ size_t *plaintext_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_key_slot_t *slot;
+
+ LOCAL_INPUT_DECLARE(nonce_external, nonce);
+ LOCAL_INPUT_DECLARE(additional_data_external, additional_data);
+ LOCAL_INPUT_DECLARE(ciphertext_external, ciphertext);
+ LOCAL_OUTPUT_DECLARE(plaintext_external, plaintext);
+
+ *plaintext_length = 0;
+
+ status = psa_aead_check_algorithm(alg);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ status = psa_get_and_lock_key_slot_with_policy(
+ key, &slot, PSA_KEY_USAGE_DECRYPT, alg);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ LOCAL_INPUT_ALLOC(nonce_external, nonce_length, nonce);
+ LOCAL_INPUT_ALLOC(additional_data_external, additional_data_length,
+ additional_data);
+ LOCAL_INPUT_ALLOC(ciphertext_external, ciphertext_length, ciphertext);
+ LOCAL_OUTPUT_ALLOC(plaintext_external, plaintext_size, plaintext);
+
+ status = psa_aead_check_nonce_length(alg, nonce_length);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ status = psa_driver_wrapper_aead_decrypt(
+ &slot->attr, slot->key.data, slot->key.bytes,
+ alg,
+ nonce, nonce_length,
+ additional_data, additional_data_length,
+ ciphertext, ciphertext_length,
+ plaintext, plaintext_size, plaintext_length);
+
+ if (status != PSA_SUCCESS && plaintext_size != 0) {
+ memset(plaintext, 0, plaintext_size);
+ }
+
+exit:
+ LOCAL_INPUT_FREE(nonce_external, nonce);
+ LOCAL_INPUT_FREE(additional_data_external, additional_data);
+ LOCAL_INPUT_FREE(ciphertext_external, ciphertext);
+ LOCAL_OUTPUT_FREE(plaintext_external, plaintext);
+
+ psa_unregister_read_under_mutex(slot);
+
+ return status;
+}
+
+static psa_status_t psa_validate_tag_length(psa_algorithm_t alg)
+{
+ const uint8_t tag_len = PSA_ALG_AEAD_GET_TAG_LENGTH(alg);
+
+ switch (PSA_ALG_AEAD_WITH_SHORTENED_TAG(alg, 0)) {
+#if defined(PSA_WANT_ALG_CCM)
+ case PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, 0):
+ /* CCM allows the following tag lengths: 4, 6, 8, 10, 12, 14, 16.*/
+ if (tag_len < 4 || tag_len > 16 || tag_len % 2) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+ break;
+#endif /* PSA_WANT_ALG_CCM */
+
+#if defined(PSA_WANT_ALG_GCM)
+ case PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_GCM, 0):
+ /* GCM allows the following tag lengths: 4, 8, 12, 13, 14, 15, 16. */
+ if (tag_len != 4 && tag_len != 8 && (tag_len < 12 || tag_len > 16)) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+ break;
+#endif /* PSA_WANT_ALG_GCM */
+
+#if defined(PSA_WANT_ALG_CHACHA20_POLY1305)
+ case PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CHACHA20_POLY1305, 0):
+ /* We only support the default tag length. */
+ if (tag_len != 16) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+ break;
+#endif /* PSA_WANT_ALG_CHACHA20_POLY1305 */
+
+ default:
+ (void) tag_len;
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+ return PSA_SUCCESS;
+}
+
+/* Set the key for a multipart authenticated operation. */
+static psa_status_t psa_aead_setup(psa_aead_operation_t *operation,
+ int is_encrypt,
+ mbedtls_svc_key_id_t key,
+ psa_algorithm_t alg)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_key_slot_t *slot = NULL;
+ psa_key_usage_t key_usage = 0;
+
+ status = psa_aead_check_algorithm(alg);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ if (operation->id != 0) {
+ status = PSA_ERROR_BAD_STATE;
+ goto exit;
+ }
+
+ if (operation->nonce_set || operation->lengths_set ||
+ operation->ad_started || operation->body_started) {
+ status = PSA_ERROR_BAD_STATE;
+ goto exit;
+ }
+
+ if (is_encrypt) {
+ key_usage = PSA_KEY_USAGE_ENCRYPT;
+ } else {
+ key_usage = PSA_KEY_USAGE_DECRYPT;
+ }
+
+ status = psa_get_and_lock_key_slot_with_policy(key, &slot, key_usage,
+ alg);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ if ((status = psa_validate_tag_length(alg)) != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ if (is_encrypt) {
+ status = psa_driver_wrapper_aead_encrypt_setup(operation,
+ &slot->attr,
+ slot->key.data,
+ slot->key.bytes,
+ alg);
+ } else {
+ status = psa_driver_wrapper_aead_decrypt_setup(operation,
+ &slot->attr,
+ slot->key.data,
+ slot->key.bytes,
+ alg);
+ }
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ operation->key_type = psa_get_key_type(&slot->attr);
+
+exit:
+ unlock_status = psa_unregister_read_under_mutex(slot);
+
+ if (status == PSA_SUCCESS) {
+ status = unlock_status;
+ operation->alg = psa_aead_get_base_algorithm(alg);
+ operation->is_encrypt = is_encrypt;
+ } else {
+ psa_aead_abort(operation);
+ }
+
+ return status;
+}
+
+/* Set the key for a multipart authenticated encryption operation. */
+psa_status_t psa_aead_encrypt_setup(psa_aead_operation_t *operation,
+ mbedtls_svc_key_id_t key,
+ psa_algorithm_t alg)
+{
+ return psa_aead_setup(operation, 1, key, alg);
+}
+
+/* Set the key for a multipart authenticated decryption operation. */
+psa_status_t psa_aead_decrypt_setup(psa_aead_operation_t *operation,
+ mbedtls_svc_key_id_t key,
+ psa_algorithm_t alg)
+{
+ return psa_aead_setup(operation, 0, key, alg);
+}
+
+static psa_status_t psa_aead_set_nonce_internal(psa_aead_operation_t *operation,
+ const uint8_t *nonce,
+ size_t nonce_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+ if (operation->id == 0) {
+ status = PSA_ERROR_BAD_STATE;
+ goto exit;
+ }
+
+ if (operation->nonce_set) {
+ status = PSA_ERROR_BAD_STATE;
+ goto exit;
+ }
+
+ status = psa_aead_check_nonce_length(operation->alg, nonce_length);
+ if (status != PSA_SUCCESS) {
+ status = PSA_ERROR_INVALID_ARGUMENT;
+ goto exit;
+ }
+
+ status = psa_driver_wrapper_aead_set_nonce(operation, nonce,
+ nonce_length);
+
+exit:
+ if (status == PSA_SUCCESS) {
+ operation->nonce_set = 1;
+ } else {
+ psa_aead_abort(operation);
+ }
+
+ return status;
+}
+
+/* Generate a random nonce / IV for multipart AEAD operation */
+psa_status_t psa_aead_generate_nonce(psa_aead_operation_t *operation,
+ uint8_t *nonce_external,
+ size_t nonce_size,
+ size_t *nonce_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ uint8_t local_nonce[PSA_AEAD_NONCE_MAX_SIZE];
+ size_t required_nonce_size = 0;
+
+ LOCAL_OUTPUT_DECLARE(nonce_external, nonce);
+ LOCAL_OUTPUT_ALLOC(nonce_external, nonce_size, nonce);
+
+ *nonce_length = 0;
+
+ if (operation->id == 0) {
+ status = PSA_ERROR_BAD_STATE;
+ goto exit;
+ }
+
+ if (operation->nonce_set || !operation->is_encrypt) {
+ status = PSA_ERROR_BAD_STATE;
+ goto exit;
+ }
+
+ /* For CCM, this size may not be correct according to the PSA
+ * specification. The PSA Crypto 1.0.1 specification states:
+ *
+ * CCM encodes the plaintext length pLen in L octets, with L the smallest
+ * integer >= 2 where pLen < 2^(8L). The nonce length is then 15 - L bytes.
+ *
+ * However this restriction that L has to be the smallest integer is not
+ * applied in practice, and it is not implementable here since the
+ * plaintext length may or may not be known at this time. */
+ required_nonce_size = PSA_AEAD_NONCE_LENGTH(operation->key_type,
+ operation->alg);
+ if (nonce_size < required_nonce_size) {
+ status = PSA_ERROR_BUFFER_TOO_SMALL;
+ goto exit;
+ }
+
+ status = psa_generate_random_internal(local_nonce, required_nonce_size);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ status = psa_aead_set_nonce_internal(operation, local_nonce,
+ required_nonce_size);
+
+exit:
+ if (status == PSA_SUCCESS) {
+ memcpy(nonce, local_nonce, required_nonce_size);
+ *nonce_length = required_nonce_size;
+ } else {
+ psa_aead_abort(operation);
+ }
+
+ LOCAL_OUTPUT_FREE(nonce_external, nonce);
+
+ return status;
+}
+
+/* Set the nonce for a multipart authenticated encryption or decryption
+ operation.*/
+psa_status_t psa_aead_set_nonce(psa_aead_operation_t *operation,
+ const uint8_t *nonce_external,
+ size_t nonce_length)
+{
+ psa_status_t status;
+
+ LOCAL_INPUT_DECLARE(nonce_external, nonce);
+ LOCAL_INPUT_ALLOC(nonce_external, nonce_length, nonce);
+
+ status = psa_aead_set_nonce_internal(operation, nonce, nonce_length);
+
+/* Exit label is only needed for buffer copying, prevent unused warnings. */
+#if !defined(MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS)
+exit:
+#endif
+
+ LOCAL_INPUT_FREE(nonce_external, nonce);
+
+ return status;
+}
+
+/* Declare the lengths of the message and additional data for multipart AEAD. */
+psa_status_t psa_aead_set_lengths(psa_aead_operation_t *operation,
+ size_t ad_length,
+ size_t plaintext_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+ if (operation->id == 0) {
+ status = PSA_ERROR_BAD_STATE;
+ goto exit;
+ }
+
+ if (operation->lengths_set || operation->ad_started ||
+ operation->body_started) {
+ status = PSA_ERROR_BAD_STATE;
+ goto exit;
+ }
+
+ switch (operation->alg) {
+#if defined(PSA_WANT_ALG_GCM)
+ case PSA_ALG_GCM:
+ /* Lengths can only be too large for GCM if size_t is bigger than 32
+ * bits. Without the guard this code will generate warnings on 32bit
+ * builds. */
+#if SIZE_MAX > UINT32_MAX
+ if (((uint64_t) ad_length) >> 61 != 0 ||
+ ((uint64_t) plaintext_length) > 0xFFFFFFFE0ull) {
+ status = PSA_ERROR_INVALID_ARGUMENT;
+ goto exit;
+ }
+#endif
+ break;
+#endif /* PSA_WANT_ALG_GCM */
+#if defined(PSA_WANT_ALG_CCM)
+ case PSA_ALG_CCM:
+ if (ad_length > 0xFF00) {
+ status = PSA_ERROR_INVALID_ARGUMENT;
+ goto exit;
+ }
+ break;
+#endif /* PSA_WANT_ALG_CCM */
+#if defined(PSA_WANT_ALG_CHACHA20_POLY1305)
+ case PSA_ALG_CHACHA20_POLY1305:
+ /* No length restrictions for ChaChaPoly. */
+ break;
+#endif /* PSA_WANT_ALG_CHACHA20_POLY1305 */
+ default:
+ break;
+ }
+
+ status = psa_driver_wrapper_aead_set_lengths(operation, ad_length,
+ plaintext_length);
+
+exit:
+ if (status == PSA_SUCCESS) {
+ operation->ad_remaining = ad_length;
+ operation->body_remaining = plaintext_length;
+ operation->lengths_set = 1;
+ } else {
+ psa_aead_abort(operation);
+ }
+
+ return status;
+}
+
+/* Pass additional data to an active multipart AEAD operation. */
+psa_status_t psa_aead_update_ad(psa_aead_operation_t *operation,
+ const uint8_t *input_external,
+ size_t input_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+ LOCAL_INPUT_DECLARE(input_external, input);
+ LOCAL_INPUT_ALLOC(input_external, input_length, input);
+
+ if (operation->id == 0) {
+ status = PSA_ERROR_BAD_STATE;
+ goto exit;
+ }
+
+ if (!operation->nonce_set || operation->body_started) {
+ status = PSA_ERROR_BAD_STATE;
+ goto exit;
+ }
+
+ /* No input to add (zero length), nothing to do. */
+ if (input_length == 0) {
+ status = PSA_SUCCESS;
+ goto exit;
+ }
+
+ if (operation->lengths_set) {
+ if (operation->ad_remaining < input_length) {
+ status = PSA_ERROR_INVALID_ARGUMENT;
+ goto exit;
+ }
+
+ operation->ad_remaining -= input_length;
+ }
+#if defined(PSA_WANT_ALG_CCM)
+ else if (operation->alg == PSA_ALG_CCM) {
+ status = PSA_ERROR_BAD_STATE;
+ goto exit;
+ }
+#endif /* PSA_WANT_ALG_CCM */
+
+ status = psa_driver_wrapper_aead_update_ad(operation, input,
+ input_length);
+
+exit:
+ if (status == PSA_SUCCESS) {
+ operation->ad_started = 1;
+ } else {
+ psa_aead_abort(operation);
+ }
+
+ LOCAL_INPUT_FREE(input_external, input);
+
+ return status;
+}
+
+/* Encrypt or decrypt a message fragment in an active multipart AEAD
+ operation.*/
+psa_status_t psa_aead_update(psa_aead_operation_t *operation,
+ const uint8_t *input_external,
+ size_t input_length,
+ uint8_t *output_external,
+ size_t output_size,
+ size_t *output_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+
+ LOCAL_INPUT_DECLARE(input_external, input);
+ LOCAL_OUTPUT_DECLARE(output_external, output);
+
+ LOCAL_INPUT_ALLOC(input_external, input_length, input);
+ LOCAL_OUTPUT_ALLOC(output_external, output_size, output);
+
+ *output_length = 0;
+
+ if (operation->id == 0) {
+ status = PSA_ERROR_BAD_STATE;
+ goto exit;
+ }
+
+ if (!operation->nonce_set) {
+ status = PSA_ERROR_BAD_STATE;
+ goto exit;
+ }
+
+ if (operation->lengths_set) {
+ /* Additional data length was supplied, but not all the additional
+ data was supplied.*/
+ if (operation->ad_remaining != 0) {
+ status = PSA_ERROR_INVALID_ARGUMENT;
+ goto exit;
+ }
+
+ /* Too much data provided. */
+ if (operation->body_remaining < input_length) {
+ status = PSA_ERROR_INVALID_ARGUMENT;
+ goto exit;
+ }
+
+ operation->body_remaining -= input_length;
+ }
+#if defined(PSA_WANT_ALG_CCM)
+ else if (operation->alg == PSA_ALG_CCM) {
+ status = PSA_ERROR_BAD_STATE;
+ goto exit;
+ }
+#endif /* PSA_WANT_ALG_CCM */
+
+ status = psa_driver_wrapper_aead_update(operation, input, input_length,
+ output, output_size,
+ output_length);
+
+exit:
+ if (status == PSA_SUCCESS) {
+ operation->body_started = 1;
+ } else {
+ psa_aead_abort(operation);
+ }
+
+ LOCAL_INPUT_FREE(input_external, input);
+ LOCAL_OUTPUT_FREE(output_external, output);
+
+ return status;
+}
+
+static psa_status_t psa_aead_final_checks(const psa_aead_operation_t *operation)
+{
+ if (operation->id == 0 || !operation->nonce_set) {
+ return PSA_ERROR_BAD_STATE;
+ }
+
+ if (operation->lengths_set && (operation->ad_remaining != 0 ||
+ operation->body_remaining != 0)) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ return PSA_SUCCESS;
+}
+
+/* Finish encrypting a message in a multipart AEAD operation. */
+psa_status_t psa_aead_finish(psa_aead_operation_t *operation,
+ uint8_t *ciphertext_external,
+ size_t ciphertext_size,
+ size_t *ciphertext_length,
+ uint8_t *tag_external,
+ size_t tag_size,
+ size_t *tag_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+ LOCAL_OUTPUT_DECLARE(ciphertext_external, ciphertext);
+ LOCAL_OUTPUT_DECLARE(tag_external, tag);
+
+ LOCAL_OUTPUT_ALLOC(ciphertext_external, ciphertext_size, ciphertext);
+ LOCAL_OUTPUT_ALLOC(tag_external, tag_size, tag);
+
+ *ciphertext_length = 0;
+ *tag_length = tag_size;
+
+ status = psa_aead_final_checks(operation);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ if (!operation->is_encrypt) {
+ status = PSA_ERROR_BAD_STATE;
+ goto exit;
+ }
+
+ status = psa_driver_wrapper_aead_finish(operation, ciphertext,
+ ciphertext_size,
+ ciphertext_length,
+ tag, tag_size, tag_length);
+
+exit:
+
+
+ /* In case the operation fails and the user fails to check for failure or
+ * the zero tag size, make sure the tag is set to something implausible.
+ * Even if the operation succeeds, make sure we clear the rest of the
+ * buffer to prevent potential leakage of anything previously placed in
+ * the same buffer.*/
+ psa_wipe_tag_output_buffer(tag, status, tag_size, *tag_length);
+
+ psa_aead_abort(operation);
+
+ LOCAL_OUTPUT_FREE(ciphertext_external, ciphertext);
+ LOCAL_OUTPUT_FREE(tag_external, tag);
+
+ return status;
+}
+
+/* Finish authenticating and decrypting a message in a multipart AEAD
+ operation.*/
+psa_status_t psa_aead_verify(psa_aead_operation_t *operation,
+ uint8_t *plaintext_external,
+ size_t plaintext_size,
+ size_t *plaintext_length,
+ const uint8_t *tag_external,
+ size_t tag_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+ LOCAL_OUTPUT_DECLARE(plaintext_external, plaintext);
+ LOCAL_INPUT_DECLARE(tag_external, tag);
+
+ LOCAL_OUTPUT_ALLOC(plaintext_external, plaintext_size, plaintext);
+ LOCAL_INPUT_ALLOC(tag_external, tag_length, tag);
+
+ *plaintext_length = 0;
+
+ status = psa_aead_final_checks(operation);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ if (operation->is_encrypt) {
+ status = PSA_ERROR_BAD_STATE;
+ goto exit;
+ }
+
+ status = psa_driver_wrapper_aead_verify(operation, plaintext,
+ plaintext_size,
+ plaintext_length,
+ tag, tag_length);
+
+exit:
+ psa_aead_abort(operation);
+
+ LOCAL_OUTPUT_FREE(plaintext_external, plaintext);
+ LOCAL_INPUT_FREE(tag_external, tag);
+
+ return status;
+}
+
+/* Abort an AEAD operation. */
+psa_status_t psa_aead_abort(psa_aead_operation_t *operation)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+ if (operation->id == 0) {
+ /* The object has (apparently) been initialized but it is not (yet)
+ * in use. It's ok to call abort on such an object, and there's
+ * nothing to do. */
+ return PSA_SUCCESS;
+ }
+
+ status = psa_driver_wrapper_aead_abort(operation);
+
+ memset(operation, 0, sizeof(*operation));
+
+ return status;
+}
+
+/****************************************************************/
+/* Generators */
+/****************************************************************/
+
+#if defined(BUILTIN_ALG_ANY_HKDF) || \
+ defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PRF) || \
+ defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS) || \
+ defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_ECJPAKE_TO_PMS) || \
+ defined(PSA_HAVE_SOFT_PBKDF2)
+#define AT_LEAST_ONE_BUILTIN_KDF
+#endif /* At least one builtin KDF */
+
+#if defined(BUILTIN_ALG_ANY_HKDF) || \
+ defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PRF) || \
+ defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS)
+static psa_status_t psa_key_derivation_start_hmac(
+ psa_mac_operation_t *operation,
+ psa_algorithm_t hash_alg,
+ const uint8_t *hmac_key,
+ size_t hmac_key_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
+ psa_set_key_type(&attributes, PSA_KEY_TYPE_HMAC);
+ psa_set_key_bits(&attributes, PSA_BYTES_TO_BITS(hmac_key_length));
+ psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_SIGN_HASH);
+
+ operation->is_sign = 1;
+ operation->mac_size = PSA_HASH_LENGTH(hash_alg);
+
+ status = psa_driver_wrapper_mac_sign_setup(operation,
+ &attributes,
+ hmac_key, hmac_key_length,
+ PSA_ALG_HMAC(hash_alg));
+
+ psa_reset_key_attributes(&attributes);
+ return status;
+}
+#endif /* KDF algorithms reliant on HMAC */
+
+#define HKDF_STATE_INIT 0 /* no input yet */
+#define HKDF_STATE_STARTED 1 /* got salt */
+#define HKDF_STATE_KEYED 2 /* got key */
+#define HKDF_STATE_OUTPUT 3 /* output started */
+
+static psa_algorithm_t psa_key_derivation_get_kdf_alg(
+ const psa_key_derivation_operation_t *operation)
+{
+ if (PSA_ALG_IS_KEY_AGREEMENT(operation->alg)) {
+ return PSA_ALG_KEY_AGREEMENT_GET_KDF(operation->alg);
+ } else {
+ return operation->alg;
+ }
+}
+
+psa_status_t psa_key_derivation_abort(psa_key_derivation_operation_t *operation)
+{
+ psa_status_t status = PSA_SUCCESS;
+ psa_algorithm_t kdf_alg = psa_key_derivation_get_kdf_alg(operation);
+ if (kdf_alg == 0) {
+ /* The object has (apparently) been initialized but it is not
+ * in use. It's ok to call abort on such an object, and there's
+ * nothing to do. */
+ } else
+#if defined(BUILTIN_ALG_ANY_HKDF)
+ if (PSA_ALG_IS_ANY_HKDF(kdf_alg)) {
+ mbedtls_free(operation->ctx.hkdf.info);
+ status = psa_mac_abort(&operation->ctx.hkdf.hmac);
+ } else
+#endif /* BUILTIN_ALG_ANY_HKDF */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PRF) || \
+ defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS)
+ if (PSA_ALG_IS_TLS12_PRF(kdf_alg) ||
+ /* TLS-1.2 PSK-to-MS KDF uses the same core as TLS-1.2 PRF */
+ PSA_ALG_IS_TLS12_PSK_TO_MS(kdf_alg)) {
+ if (operation->ctx.tls12_prf.secret != NULL) {
+ mbedtls_zeroize_and_free(operation->ctx.tls12_prf.secret,
+ operation->ctx.tls12_prf.secret_length);
+ }
+
+ if (operation->ctx.tls12_prf.seed != NULL) {
+ mbedtls_zeroize_and_free(operation->ctx.tls12_prf.seed,
+ operation->ctx.tls12_prf.seed_length);
+ }
+
+ if (operation->ctx.tls12_prf.label != NULL) {
+ mbedtls_zeroize_and_free(operation->ctx.tls12_prf.label,
+ operation->ctx.tls12_prf.label_length);
+ }
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS)
+ if (operation->ctx.tls12_prf.other_secret != NULL) {
+ mbedtls_zeroize_and_free(operation->ctx.tls12_prf.other_secret,
+ operation->ctx.tls12_prf.other_secret_length);
+ }
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS */
+ status = PSA_SUCCESS;
+
+ /* We leave the fields Ai and output_block to be erased safely by the
+ * mbedtls_platform_zeroize() in the end of this function. */
+ } else
+#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PRF) ||
+ * defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS) */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_ECJPAKE_TO_PMS)
+ if (kdf_alg == PSA_ALG_TLS12_ECJPAKE_TO_PMS) {
+ mbedtls_platform_zeroize(operation->ctx.tls12_ecjpake_to_pms.data,
+ sizeof(operation->ctx.tls12_ecjpake_to_pms.data));
+ } else
+#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_ECJPAKE_TO_PMS) */
+#if defined(PSA_HAVE_SOFT_PBKDF2)
+ if (PSA_ALG_IS_PBKDF2(kdf_alg)) {
+ if (operation->ctx.pbkdf2.salt != NULL) {
+ mbedtls_zeroize_and_free(operation->ctx.pbkdf2.salt,
+ operation->ctx.pbkdf2.salt_length);
+ }
+
+ status = PSA_SUCCESS;
+ } else
+#endif /* defined(PSA_HAVE_SOFT_PBKDF2) */
+ {
+ status = PSA_ERROR_BAD_STATE;
+ }
+ mbedtls_platform_zeroize(operation, sizeof(*operation));
+ return status;
+}
+
+psa_status_t psa_key_derivation_get_capacity(const psa_key_derivation_operation_t *operation,
+ size_t *capacity)
+{
+ if (operation->alg == 0) {
+ /* This is a blank key derivation operation. */
+ return PSA_ERROR_BAD_STATE;
+ }
+
+ *capacity = operation->capacity;
+ return PSA_SUCCESS;
+}
+
+psa_status_t psa_key_derivation_set_capacity(psa_key_derivation_operation_t *operation,
+ size_t capacity)
+{
+ if (operation->alg == 0) {
+ return PSA_ERROR_BAD_STATE;
+ }
+ if (capacity > operation->capacity) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+ operation->capacity = capacity;
+ return PSA_SUCCESS;
+}
+
+#if defined(BUILTIN_ALG_ANY_HKDF)
+/* Read some bytes from an HKDF-based operation. */
+static psa_status_t psa_key_derivation_hkdf_read(psa_hkdf_key_derivation_t *hkdf,
+ psa_algorithm_t kdf_alg,
+ uint8_t *output,
+ size_t output_length)
+{
+ psa_algorithm_t hash_alg = PSA_ALG_HKDF_GET_HASH(kdf_alg);
+ uint8_t hash_length = PSA_HASH_LENGTH(hash_alg);
+ size_t hmac_output_length;
+ psa_status_t status;
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT)
+ const uint8_t last_block = PSA_ALG_IS_HKDF_EXTRACT(kdf_alg) ? 0 : 0xff;
+#else
+ const uint8_t last_block = 0xff;
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT */
+
+ if (hkdf->state < HKDF_STATE_KEYED ||
+ (!hkdf->info_set
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT)
+ && !PSA_ALG_IS_HKDF_EXTRACT(kdf_alg)
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT */
+ )) {
+ return PSA_ERROR_BAD_STATE;
+ }
+ hkdf->state = HKDF_STATE_OUTPUT;
+
+ while (output_length != 0) {
+ /* Copy what remains of the current block */
+ uint8_t n = hash_length - hkdf->offset_in_block;
+ if (n > output_length) {
+ n = (uint8_t) output_length;
+ }
+ memcpy(output, hkdf->output_block + hkdf->offset_in_block, n);
+ output += n;
+ output_length -= n;
+ hkdf->offset_in_block += n;
+ if (output_length == 0) {
+ break;
+ }
+ /* We can't be wanting more output after the last block, otherwise
+ * the capacity check in psa_key_derivation_output_bytes() would have
+ * prevented this call. It could happen only if the operation
+ * object was corrupted or if this function is called directly
+ * inside the library. */
+ if (hkdf->block_number == last_block) {
+ return PSA_ERROR_BAD_STATE;
+ }
+
+ /* We need a new block */
+ ++hkdf->block_number;
+ hkdf->offset_in_block = 0;
+
+ status = psa_key_derivation_start_hmac(&hkdf->hmac,
+ hash_alg,
+ hkdf->prk,
+ hash_length);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ if (hkdf->block_number != 1) {
+ status = psa_mac_update(&hkdf->hmac,
+ hkdf->output_block,
+ hash_length);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+ }
+ status = psa_mac_update(&hkdf->hmac,
+ hkdf->info,
+ hkdf->info_length);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+ status = psa_mac_update(&hkdf->hmac,
+ &hkdf->block_number, 1);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+ status = psa_mac_sign_finish(&hkdf->hmac,
+ hkdf->output_block,
+ sizeof(hkdf->output_block),
+ &hmac_output_length);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+ }
+
+ return PSA_SUCCESS;
+}
+#endif /* BUILTIN_ALG_ANY_HKDF */
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PRF) || \
+ defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS)
+static psa_status_t psa_key_derivation_tls12_prf_generate_next_block(
+ psa_tls12_prf_key_derivation_t *tls12_prf,
+ psa_algorithm_t alg)
+{
+ psa_algorithm_t hash_alg = PSA_ALG_HKDF_GET_HASH(alg);
+ uint8_t hash_length = PSA_HASH_LENGTH(hash_alg);
+ psa_mac_operation_t hmac = PSA_MAC_OPERATION_INIT;
+ size_t hmac_output_length;
+ psa_status_t status, cleanup_status;
+
+ /* We can't be wanting more output after block 0xff, otherwise
+ * the capacity check in psa_key_derivation_output_bytes() would have
+ * prevented this call. It could happen only if the operation
+ * object was corrupted or if this function is called directly
+ * inside the library. */
+ if (tls12_prf->block_number == 0xff) {
+ return PSA_ERROR_CORRUPTION_DETECTED;
+ }
+
+ /* We need a new block */
+ ++tls12_prf->block_number;
+ tls12_prf->left_in_block = hash_length;
+
+ /* Recall the definition of the TLS-1.2-PRF from RFC 5246:
+ *
+ * PRF(secret, label, seed) = P_<hash>(secret, label + seed)
+ *
+ * P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) +
+ * HMAC_hash(secret, A(2) + seed) +
+ * HMAC_hash(secret, A(3) + seed) + ...
+ *
+ * A(0) = seed
+ * A(i) = HMAC_hash(secret, A(i-1))
+ *
+ * The `psa_tls12_prf_key_derivation` structure saves the block
+ * `HMAC_hash(secret, A(i) + seed)` from which the output
+ * is currently extracted as `output_block` and where i is
+ * `block_number`.
+ */
+
+ status = psa_key_derivation_start_hmac(&hmac,
+ hash_alg,
+ tls12_prf->secret,
+ tls12_prf->secret_length);
+ if (status != PSA_SUCCESS) {
+ goto cleanup;
+ }
+
+ /* Calculate A(i) where i = tls12_prf->block_number. */
+ if (tls12_prf->block_number == 1) {
+ /* A(1) = HMAC_hash(secret, A(0)), where A(0) = seed. (The RFC overloads
+ * the variable seed and in this instance means it in the context of the
+ * P_hash function, where seed = label + seed.) */
+ status = psa_mac_update(&hmac,
+ tls12_prf->label,
+ tls12_prf->label_length);
+ if (status != PSA_SUCCESS) {
+ goto cleanup;
+ }
+ status = psa_mac_update(&hmac,
+ tls12_prf->seed,
+ tls12_prf->seed_length);
+ if (status != PSA_SUCCESS) {
+ goto cleanup;
+ }
+ } else {
+ /* A(i) = HMAC_hash(secret, A(i-1)) */
+ status = psa_mac_update(&hmac, tls12_prf->Ai, hash_length);
+ if (status != PSA_SUCCESS) {
+ goto cleanup;
+ }
+ }
+
+ status = psa_mac_sign_finish(&hmac,
+ tls12_prf->Ai, hash_length,
+ &hmac_output_length);
+ if (hmac_output_length != hash_length) {
+ status = PSA_ERROR_CORRUPTION_DETECTED;
+ }
+ if (status != PSA_SUCCESS) {
+ goto cleanup;
+ }
+
+ /* Calculate HMAC_hash(secret, A(i) + label + seed). */
+ status = psa_key_derivation_start_hmac(&hmac,
+ hash_alg,
+ tls12_prf->secret,
+ tls12_prf->secret_length);
+ if (status != PSA_SUCCESS) {
+ goto cleanup;
+ }
+ status = psa_mac_update(&hmac, tls12_prf->Ai, hash_length);
+ if (status != PSA_SUCCESS) {
+ goto cleanup;
+ }
+ status = psa_mac_update(&hmac, tls12_prf->label, tls12_prf->label_length);
+ if (status != PSA_SUCCESS) {
+ goto cleanup;
+ }
+ status = psa_mac_update(&hmac, tls12_prf->seed, tls12_prf->seed_length);
+ if (status != PSA_SUCCESS) {
+ goto cleanup;
+ }
+ status = psa_mac_sign_finish(&hmac,
+ tls12_prf->output_block, hash_length,
+ &hmac_output_length);
+ if (status != PSA_SUCCESS) {
+ goto cleanup;
+ }
+
+
+cleanup:
+ cleanup_status = psa_mac_abort(&hmac);
+ if (status == PSA_SUCCESS && cleanup_status != PSA_SUCCESS) {
+ status = cleanup_status;
+ }
+
+ return status;
+}
+
+static psa_status_t psa_key_derivation_tls12_prf_read(
+ psa_tls12_prf_key_derivation_t *tls12_prf,
+ psa_algorithm_t alg,
+ uint8_t *output,
+ size_t output_length)
+{
+ psa_algorithm_t hash_alg = PSA_ALG_TLS12_PRF_GET_HASH(alg);
+ uint8_t hash_length = PSA_HASH_LENGTH(hash_alg);
+ psa_status_t status;
+ uint8_t offset, length;
+
+ switch (tls12_prf->state) {
+ case PSA_TLS12_PRF_STATE_LABEL_SET:
+ tls12_prf->state = PSA_TLS12_PRF_STATE_OUTPUT;
+ break;
+ case PSA_TLS12_PRF_STATE_OUTPUT:
+ break;
+ default:
+ return PSA_ERROR_BAD_STATE;
+ }
+
+ while (output_length != 0) {
+ /* Check if we have fully processed the current block. */
+ if (tls12_prf->left_in_block == 0) {
+ status = psa_key_derivation_tls12_prf_generate_next_block(tls12_prf,
+ alg);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ continue;
+ }
+
+ if (tls12_prf->left_in_block > output_length) {
+ length = (uint8_t) output_length;
+ } else {
+ length = tls12_prf->left_in_block;
+ }
+
+ offset = hash_length - tls12_prf->left_in_block;
+ memcpy(output, tls12_prf->output_block + offset, length);
+ output += length;
+ output_length -= length;
+ tls12_prf->left_in_block -= length;
+ }
+
+ return PSA_SUCCESS;
+}
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_TLS12_PRF ||
+ * MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS */
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_ECJPAKE_TO_PMS)
+static psa_status_t psa_key_derivation_tls12_ecjpake_to_pms_read(
+ psa_tls12_ecjpake_to_pms_t *ecjpake,
+ uint8_t *output,
+ size_t output_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ size_t output_size = 0;
+
+ if (output_length != 32) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ status = psa_hash_compute(PSA_ALG_SHA_256, ecjpake->data,
+ PSA_TLS12_ECJPAKE_TO_PMS_DATA_SIZE, output, output_length,
+ &output_size);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ if (output_size != output_length) {
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+
+ return PSA_SUCCESS;
+}
+#endif
+
+#if defined(PSA_HAVE_SOFT_PBKDF2)
+static psa_status_t psa_key_derivation_pbkdf2_generate_block(
+ psa_pbkdf2_key_derivation_t *pbkdf2,
+ psa_algorithm_t prf_alg,
+ uint8_t prf_output_length,
+ psa_key_attributes_t *attributes)
+{
+ psa_status_t status;
+ psa_mac_operation_t mac_operation = PSA_MAC_OPERATION_INIT;
+ size_t mac_output_length;
+ uint8_t U_i[PSA_MAC_MAX_SIZE];
+ uint8_t *U_accumulator = pbkdf2->output_block;
+ uint64_t i;
+ uint8_t block_counter[4];
+
+ mac_operation.is_sign = 1;
+ mac_operation.mac_size = prf_output_length;
+ MBEDTLS_PUT_UINT32_BE(pbkdf2->block_number, block_counter, 0);
+
+ status = psa_driver_wrapper_mac_sign_setup(&mac_operation,
+ attributes,
+ pbkdf2->password,
+ pbkdf2->password_length,
+ prf_alg);
+ if (status != PSA_SUCCESS) {
+ goto cleanup;
+ }
+ status = psa_mac_update(&mac_operation, pbkdf2->salt, pbkdf2->salt_length);
+ if (status != PSA_SUCCESS) {
+ goto cleanup;
+ }
+ status = psa_mac_update(&mac_operation, block_counter, sizeof(block_counter));
+ if (status != PSA_SUCCESS) {
+ goto cleanup;
+ }
+ status = psa_mac_sign_finish(&mac_operation, U_i, sizeof(U_i),
+ &mac_output_length);
+ if (status != PSA_SUCCESS) {
+ goto cleanup;
+ }
+
+ if (mac_output_length != prf_output_length) {
+ status = PSA_ERROR_CORRUPTION_DETECTED;
+ goto cleanup;
+ }
+
+ memcpy(U_accumulator, U_i, prf_output_length);
+
+ for (i = 1; i < pbkdf2->input_cost; i++) {
+ /* We are passing prf_output_length as mac_size because the driver
+ * function directly sets mac_output_length as mac_size upon success.
+ * See https://github.com/Mbed-TLS/mbedtls/issues/7801 */
+ status = psa_driver_wrapper_mac_compute(attributes,
+ pbkdf2->password,
+ pbkdf2->password_length,
+ prf_alg, U_i, prf_output_length,
+ U_i, prf_output_length,
+ &mac_output_length);
+ if (status != PSA_SUCCESS) {
+ goto cleanup;
+ }
+
+ mbedtls_xor(U_accumulator, U_accumulator, U_i, prf_output_length);
+ }
+
+cleanup:
+ /* Zeroise buffers to clear sensitive data from memory. */
+ mbedtls_platform_zeroize(U_i, PSA_MAC_MAX_SIZE);
+ return status;
+}
+
+static psa_status_t psa_key_derivation_pbkdf2_read(
+ psa_pbkdf2_key_derivation_t *pbkdf2,
+ psa_algorithm_t kdf_alg,
+ uint8_t *output,
+ size_t output_length)
+{
+ psa_status_t status;
+ psa_algorithm_t prf_alg;
+ uint8_t prf_output_length;
+ psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
+ psa_set_key_bits(&attributes, PSA_BYTES_TO_BITS(pbkdf2->password_length));
+ psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_SIGN_MESSAGE);
+
+ if (PSA_ALG_IS_PBKDF2_HMAC(kdf_alg)) {
+ prf_alg = PSA_ALG_HMAC(PSA_ALG_PBKDF2_HMAC_GET_HASH(kdf_alg));
+ prf_output_length = PSA_HASH_LENGTH(prf_alg);
+ psa_set_key_type(&attributes, PSA_KEY_TYPE_HMAC);
+ } else if (kdf_alg == PSA_ALG_PBKDF2_AES_CMAC_PRF_128) {
+ prf_alg = PSA_ALG_CMAC;
+ prf_output_length = PSA_MAC_LENGTH(PSA_KEY_TYPE_AES, 128U, PSA_ALG_CMAC);
+ psa_set_key_type(&attributes, PSA_KEY_TYPE_AES);
+ } else {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ switch (pbkdf2->state) {
+ case PSA_PBKDF2_STATE_PASSWORD_SET:
+ /* Initially we need a new block so bytes_used is equal to block size*/
+ pbkdf2->bytes_used = prf_output_length;
+ pbkdf2->state = PSA_PBKDF2_STATE_OUTPUT;
+ break;
+ case PSA_PBKDF2_STATE_OUTPUT:
+ break;
+ default:
+ return PSA_ERROR_BAD_STATE;
+ }
+
+ while (output_length != 0) {
+ uint8_t n = prf_output_length - pbkdf2->bytes_used;
+ if (n > output_length) {
+ n = (uint8_t) output_length;
+ }
+ memcpy(output, pbkdf2->output_block + pbkdf2->bytes_used, n);
+ output += n;
+ output_length -= n;
+ pbkdf2->bytes_used += n;
+
+ if (output_length == 0) {
+ break;
+ }
+
+ /* We need a new block */
+ pbkdf2->bytes_used = 0;
+ pbkdf2->block_number++;
+
+ status = psa_key_derivation_pbkdf2_generate_block(pbkdf2, prf_alg,
+ prf_output_length,
+ &attributes);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+ }
+
+ return PSA_SUCCESS;
+}
+#endif /* PSA_HAVE_SOFT_PBKDF2 */
+
+psa_status_t psa_key_derivation_output_bytes(
+ psa_key_derivation_operation_t *operation,
+ uint8_t *output_external,
+ size_t output_length)
+{
+ psa_status_t status;
+ LOCAL_OUTPUT_DECLARE(output_external, output);
+
+ psa_algorithm_t kdf_alg = psa_key_derivation_get_kdf_alg(operation);
+
+ if (operation->alg == 0) {
+ /* This is a blank operation. */
+ return PSA_ERROR_BAD_STATE;
+ }
+
+ if (output_length == 0 && operation->capacity == 0) {
+ /* Edge case: this is a finished operation, and 0 bytes
+ * were requested. The right error in this case could
+ * be either INSUFFICIENT_CAPACITY or BAD_STATE. Return
+ * INSUFFICIENT_CAPACITY, which is right for a finished
+ * operation, for consistency with the case when
+ * output_length > 0. */
+ return PSA_ERROR_INSUFFICIENT_DATA;
+ }
+
+ LOCAL_OUTPUT_ALLOC(output_external, output_length, output);
+ if (output_length > operation->capacity) {
+ operation->capacity = 0;
+ /* Go through the error path to wipe all confidential data now
+ * that the operation object is useless. */
+ status = PSA_ERROR_INSUFFICIENT_DATA;
+ goto exit;
+ }
+
+ operation->capacity -= output_length;
+
+#if defined(BUILTIN_ALG_ANY_HKDF)
+ if (PSA_ALG_IS_ANY_HKDF(kdf_alg)) {
+ status = psa_key_derivation_hkdf_read(&operation->ctx.hkdf, kdf_alg,
+ output, output_length);
+ } else
+#endif /* BUILTIN_ALG_ANY_HKDF */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PRF) || \
+ defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS)
+ if (PSA_ALG_IS_TLS12_PRF(kdf_alg) ||
+ PSA_ALG_IS_TLS12_PSK_TO_MS(kdf_alg)) {
+ status = psa_key_derivation_tls12_prf_read(&operation->ctx.tls12_prf,
+ kdf_alg, output,
+ output_length);
+ } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_TLS12_PRF ||
+ * MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_ECJPAKE_TO_PMS)
+ if (kdf_alg == PSA_ALG_TLS12_ECJPAKE_TO_PMS) {
+ status = psa_key_derivation_tls12_ecjpake_to_pms_read(
+ &operation->ctx.tls12_ecjpake_to_pms, output, output_length);
+ } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_TLS12_ECJPAKE_TO_PMS */
+#if defined(PSA_HAVE_SOFT_PBKDF2)
+ if (PSA_ALG_IS_PBKDF2(kdf_alg)) {
+ status = psa_key_derivation_pbkdf2_read(&operation->ctx.pbkdf2, kdf_alg,
+ output, output_length);
+ } else
+#endif /* PSA_HAVE_SOFT_PBKDF2 */
+
+ {
+ (void) kdf_alg;
+ status = PSA_ERROR_BAD_STATE;
+ LOCAL_OUTPUT_FREE(output_external, output);
+
+ return status;
+ }
+
+exit:
+ if (status != PSA_SUCCESS) {
+ /* Preserve the algorithm upon errors, but clear all sensitive state.
+ * This allows us to differentiate between exhausted operations and
+ * blank operations, so we can return PSA_ERROR_BAD_STATE on blank
+ * operations. */
+ psa_algorithm_t alg = operation->alg;
+ psa_key_derivation_abort(operation);
+ operation->alg = alg;
+ if (output != NULL) {
+ memset(output, '!', output_length);
+ }
+ }
+
+ LOCAL_OUTPUT_FREE(output_external, output);
+ return status;
+}
+
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DES)
+static void psa_des_set_key_parity(uint8_t *data, size_t data_size)
+{
+ if (data_size >= 8) {
+ mbedtls_des_key_set_parity(data);
+ }
+ if (data_size >= 16) {
+ mbedtls_des_key_set_parity(data + 8);
+ }
+ if (data_size >= 24) {
+ mbedtls_des_key_set_parity(data + 16);
+ }
+}
+#endif /* MBEDTLS_PSA_BUILTIN_KEY_TYPE_DES */
+
+/*
+ * ECC keys on a Weierstrass elliptic curve require the generation
+ * of a private key which is an integer
+ * in the range [1, N - 1], where N is the boundary of the private key domain:
+ * N is the prime p for Diffie-Hellman, or the order of the
+ * curve’s base point for ECC.
+ *
+ * Let m be the bit size of N, such that 2^m > N >= 2^(m-1).
+ * This function generates the private key using the following process:
+ *
+ * 1. Draw a byte string of length ceiling(m/8) bytes.
+ * 2. If m is not a multiple of 8, set the most significant
+ * (8 * ceiling(m/8) - m) bits of the first byte in the string to zero.
+ * 3. Convert the string to integer k by decoding it as a big-endian byte string.
+ * 4. If k > N - 2, discard the result and return to step 1.
+ * 5. Output k + 1 as the private key.
+ *
+ * This method allows compliance to NIST standards, specifically the methods titled
+ * Key-Pair Generation by Testing Candidates in the following publications:
+ * - NIST Special Publication 800-56A: Recommendation for Pair-Wise Key-Establishment
+ * Schemes Using Discrete Logarithm Cryptography [SP800-56A] §5.6.1.1.4 for
+ * Diffie-Hellman keys.
+ *
+ * - [SP800-56A] §5.6.1.2.2 or FIPS Publication 186-4: Digital Signature
+ * Standard (DSS) [FIPS186-4] §B.4.2 for elliptic curve keys.
+ *
+ * Note: Function allocates memory for *data buffer, so given *data should be
+ * always NULL.
+ */
+#if defined(PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_DERIVE)
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR_DERIVE)
+static psa_status_t psa_generate_derived_ecc_key_weierstrass_helper(
+ psa_key_slot_t *slot,
+ size_t bits,
+ psa_key_derivation_operation_t *operation,
+ uint8_t **data
+ )
+{
+ unsigned key_out_of_range = 1;
+ mbedtls_mpi k;
+ mbedtls_mpi diff_N_2;
+ int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ size_t m;
+ size_t m_bytes;
+
+ mbedtls_mpi_init(&k);
+ mbedtls_mpi_init(&diff_N_2);
+
+ psa_ecc_family_t curve = PSA_KEY_TYPE_ECC_GET_FAMILY(
+ slot->attr.type);
+ mbedtls_ecp_group_id grp_id =
+ mbedtls_ecc_group_from_psa(curve, bits);
+
+ if (grp_id == MBEDTLS_ECP_DP_NONE) {
+ ret = MBEDTLS_ERR_ASN1_INVALID_DATA;
+ goto cleanup;
+ }
+
+ mbedtls_ecp_group ecp_group;
+ mbedtls_ecp_group_init(&ecp_group);
+
+ MBEDTLS_MPI_CHK(mbedtls_ecp_group_load(&ecp_group, grp_id));
+
+ /* N is the boundary of the private key domain (ecp_group.N). */
+ /* Let m be the bit size of N. */
+ m = ecp_group.nbits;
+
+ m_bytes = PSA_BITS_TO_BYTES(m);
+
+ /* Calculate N - 2 - it will be needed later. */
+ MBEDTLS_MPI_CHK(mbedtls_mpi_sub_int(&diff_N_2, &ecp_group.N, 2));
+
+ /* Note: This function is always called with *data == NULL and it
+ * allocates memory for the data buffer. */
+ *data = mbedtls_calloc(1, m_bytes);
+ if (*data == NULL) {
+ ret = MBEDTLS_ERR_ASN1_ALLOC_FAILED;
+ goto cleanup;
+ }
+
+ while (key_out_of_range) {
+ /* 1. Draw a byte string of length ceiling(m/8) bytes. */
+ if ((status = psa_key_derivation_output_bytes(operation, *data, m_bytes)) != 0) {
+ goto cleanup;
+ }
+
+ /* 2. If m is not a multiple of 8 */
+ if (m % 8 != 0) {
+ /* Set the most significant
+ * (8 * ceiling(m/8) - m) bits of the first byte in
+ * the string to zero.
+ */
+ uint8_t clear_bit_mask = (1 << (m % 8)) - 1;
+ (*data)[0] &= clear_bit_mask;
+ }
+
+ /* 3. Convert the string to integer k by decoding it as a
+ * big-endian byte string.
+ */
+ MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(&k, *data, m_bytes));
+
+ /* 4. If k > N - 2, discard the result and return to step 1.
+ * Result of comparison is returned. When it indicates error
+ * then this function is called again.
+ */
+ MBEDTLS_MPI_CHK(mbedtls_mpi_lt_mpi_ct(&diff_N_2, &k, &key_out_of_range));
+ }
+
+ /* 5. Output k + 1 as the private key. */
+ MBEDTLS_MPI_CHK(mbedtls_mpi_add_int(&k, &k, 1));
+ MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary(&k, *data, m_bytes));
+cleanup:
+ if (ret != 0) {
+ status = mbedtls_to_psa_error(ret);
+ }
+ if (status != PSA_SUCCESS) {
+ mbedtls_free(*data);
+ *data = NULL;
+ }
+ mbedtls_mpi_free(&k);
+ mbedtls_mpi_free(&diff_N_2);
+ return status;
+}
+
+/* ECC keys on a Montgomery elliptic curve draws a byte string whose length
+ * is determined by the curve, and sets the mandatory bits accordingly. That is:
+ *
+ * - Curve25519 (PSA_ECC_FAMILY_MONTGOMERY, 255 bits):
+ * draw a 32-byte string and process it as specified in
+ * Elliptic Curves for Security [RFC7748] §5.
+ *
+ * - Curve448 (PSA_ECC_FAMILY_MONTGOMERY, 448 bits):
+ * draw a 56-byte string and process it as specified in [RFC7748] §5.
+ *
+ * Note: Function allocates memory for *data buffer, so given *data should be
+ * always NULL.
+ */
+
+static psa_status_t psa_generate_derived_ecc_key_montgomery_helper(
+ size_t bits,
+ psa_key_derivation_operation_t *operation,
+ uint8_t **data
+ )
+{
+ size_t output_length;
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+ switch (bits) {
+ case 255:
+ output_length = 32;
+ break;
+ case 448:
+ output_length = 56;
+ break;
+ default:
+ return PSA_ERROR_INVALID_ARGUMENT;
+ break;
+ }
+
+ *data = mbedtls_calloc(1, output_length);
+
+ if (*data == NULL) {
+ return PSA_ERROR_INSUFFICIENT_MEMORY;
+ }
+
+ status = psa_key_derivation_output_bytes(operation, *data, output_length);
+
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ switch (bits) {
+ case 255:
+ (*data)[0] &= 248;
+ (*data)[31] &= 127;
+ (*data)[31] |= 64;
+ break;
+ case 448:
+ (*data)[0] &= 252;
+ (*data)[55] |= 128;
+ break;
+ default:
+ return PSA_ERROR_CORRUPTION_DETECTED;
+ break;
+ }
+
+ return status;
+}
+#else /* MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR_DERIVE */
+static psa_status_t psa_generate_derived_ecc_key_weierstrass_helper(
+ psa_key_slot_t *slot, size_t bits,
+ psa_key_derivation_operation_t *operation, uint8_t **data)
+{
+ (void) slot;
+ (void) bits;
+ (void) operation;
+ (void) data;
+ return PSA_ERROR_NOT_SUPPORTED;
+}
+
+static psa_status_t psa_generate_derived_ecc_key_montgomery_helper(
+ size_t bits, psa_key_derivation_operation_t *operation, uint8_t **data)
+{
+ (void) bits;
+ (void) operation;
+ (void) data;
+ return PSA_ERROR_NOT_SUPPORTED;
+}
+#endif /* MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR_DERIVE */
+#endif /* PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_DERIVE */
+
+static psa_status_t psa_generate_derived_key_internal(
+ psa_key_slot_t *slot,
+ size_t bits,
+ psa_key_derivation_operation_t *operation)
+{
+ uint8_t *data = NULL;
+ size_t bytes = PSA_BITS_TO_BYTES(bits);
+ size_t storage_size = bytes;
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+ if (PSA_KEY_TYPE_IS_PUBLIC_KEY(slot->attr.type)) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+#if defined(PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_DERIVE) || \
+ defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR_DERIVE)
+ if (PSA_KEY_TYPE_IS_ECC(slot->attr.type)) {
+ psa_ecc_family_t curve = PSA_KEY_TYPE_ECC_GET_FAMILY(slot->attr.type);
+ if (PSA_ECC_FAMILY_IS_WEIERSTRASS(curve)) {
+ /* Weierstrass elliptic curve */
+ status = psa_generate_derived_ecc_key_weierstrass_helper(slot, bits, operation, &data);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+ } else {
+ /* Montgomery elliptic curve */
+ status = psa_generate_derived_ecc_key_montgomery_helper(bits, operation, &data);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+ }
+ } else
+#endif /* defined(PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_DERIVE) ||
+ defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR_DERIVE) */
+ if (key_type_is_raw_bytes(slot->attr.type)) {
+ if (bits % 8 != 0) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+ data = mbedtls_calloc(1, bytes);
+ if (data == NULL) {
+ return PSA_ERROR_INSUFFICIENT_MEMORY;
+ }
+
+ status = psa_key_derivation_output_bytes(operation, data, bytes);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DES)
+ if (slot->attr.type == PSA_KEY_TYPE_DES) {
+ psa_des_set_key_parity(data, bytes);
+ }
+#endif /* defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DES) */
+ } else {
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+
+ slot->attr.bits = (psa_key_bits_t) bits;
+
+ if (psa_key_lifetime_is_external(slot->attr.lifetime)) {
+ status = psa_driver_wrapper_get_key_buffer_size(&slot->attr,
+ &storage_size);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+ }
+ status = psa_allocate_buffer_to_slot(slot, storage_size);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ status = psa_driver_wrapper_import_key(&slot->attr,
+ data, bytes,
+ slot->key.data,
+ slot->key.bytes,
+ &slot->key.bytes, &bits);
+ if (bits != slot->attr.bits) {
+ status = PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+exit:
+ mbedtls_free(data);
+ return status;
+}
+
+static const psa_custom_key_parameters_t default_custom_production =
+ PSA_CUSTOM_KEY_PARAMETERS_INIT;
+
+int psa_custom_key_parameters_are_default(
+ const psa_custom_key_parameters_t *custom,
+ size_t custom_data_length)
+{
+ if (custom->flags != 0) {
+ return 0;
+ }
+ if (custom_data_length != 0) {
+ return 0;
+ }
+ return 1;
+}
+
+psa_status_t psa_key_derivation_output_key_custom(
+ const psa_key_attributes_t *attributes,
+ psa_key_derivation_operation_t *operation,
+ const psa_custom_key_parameters_t *custom,
+ const uint8_t *custom_data,
+ size_t custom_data_length,
+ mbedtls_svc_key_id_t *key)
+{
+ psa_status_t status;
+ psa_key_slot_t *slot = NULL;
+ psa_se_drv_table_entry_t *driver = NULL;
+
+ *key = MBEDTLS_SVC_KEY_ID_INIT;
+
+ /* Reject any attempt to create a zero-length key so that we don't
+ * risk tripping up later, e.g. on a malloc(0) that returns NULL. */
+ if (psa_get_key_bits(attributes) == 0) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ (void) custom_data; /* We only accept 0-length data */
+ if (!psa_custom_key_parameters_are_default(custom, custom_data_length)) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ if (operation->alg == PSA_ALG_NONE) {
+ return PSA_ERROR_BAD_STATE;
+ }
+
+ if (!operation->can_output_key) {
+ return PSA_ERROR_NOT_PERMITTED;
+ }
+
+ status = psa_start_key_creation(PSA_KEY_CREATION_DERIVE, attributes,
+ &slot, &driver);
+#if defined(MBEDTLS_PSA_CRYPTO_SE_C)
+ if (driver != NULL) {
+ /* Deriving a key in a secure element is not implemented yet. */
+ status = PSA_ERROR_NOT_SUPPORTED;
+ }
+#endif /* MBEDTLS_PSA_CRYPTO_SE_C */
+ if (status == PSA_SUCCESS) {
+ status = psa_generate_derived_key_internal(slot,
+ attributes->bits,
+ operation);
+ }
+ if (status == PSA_SUCCESS) {
+ status = psa_finish_key_creation(slot, driver, key);
+ }
+ if (status != PSA_SUCCESS) {
+ psa_fail_key_creation(slot, driver);
+ }
+
+ return status;
+}
+
+psa_status_t psa_key_derivation_output_key_ext(
+ const psa_key_attributes_t *attributes,
+ psa_key_derivation_operation_t *operation,
+ const psa_key_production_parameters_t *params,
+ size_t params_data_length,
+ mbedtls_svc_key_id_t *key)
+{
+ return psa_key_derivation_output_key_custom(
+ attributes, operation,
+ (const psa_custom_key_parameters_t *) params,
+ params->data, params_data_length,
+ key);
+}
+
+psa_status_t psa_key_derivation_output_key(
+ const psa_key_attributes_t *attributes,
+ psa_key_derivation_operation_t *operation,
+ mbedtls_svc_key_id_t *key)
+{
+ return psa_key_derivation_output_key_custom(attributes, operation,
+ &default_custom_production,
+ NULL, 0,
+ key);
+}
+
+
+/****************************************************************/
+/* Key derivation */
+/****************************************************************/
+
+#if defined(AT_LEAST_ONE_BUILTIN_KDF)
+static int is_kdf_alg_supported(psa_algorithm_t kdf_alg)
+{
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF)
+ if (PSA_ALG_IS_HKDF(kdf_alg)) {
+ return 1;
+ }
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT)
+ if (PSA_ALG_IS_HKDF_EXTRACT(kdf_alg)) {
+ return 1;
+ }
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXPAND)
+ if (PSA_ALG_IS_HKDF_EXPAND(kdf_alg)) {
+ return 1;
+ }
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PRF)
+ if (PSA_ALG_IS_TLS12_PRF(kdf_alg)) {
+ return 1;
+ }
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS)
+ if (PSA_ALG_IS_TLS12_PSK_TO_MS(kdf_alg)) {
+ return 1;
+ }
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_ECJPAKE_TO_PMS)
+ if (kdf_alg == PSA_ALG_TLS12_ECJPAKE_TO_PMS) {
+ return 1;
+ }
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_PBKDF2_HMAC)
+ if (PSA_ALG_IS_PBKDF2_HMAC(kdf_alg)) {
+ return 1;
+ }
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_PBKDF2_AES_CMAC_PRF_128)
+ if (kdf_alg == PSA_ALG_PBKDF2_AES_CMAC_PRF_128) {
+ return 1;
+ }
+#endif
+ return 0;
+}
+
+static psa_status_t psa_hash_try_support(psa_algorithm_t alg)
+{
+ psa_hash_operation_t operation = PSA_HASH_OPERATION_INIT;
+ psa_status_t status = psa_hash_setup(&operation, alg);
+ psa_hash_abort(&operation);
+ return status;
+}
+
+static psa_status_t psa_key_derivation_set_maximum_capacity(
+ psa_key_derivation_operation_t *operation,
+ psa_algorithm_t kdf_alg)
+{
+#if defined(PSA_WANT_ALG_TLS12_ECJPAKE_TO_PMS)
+ if (kdf_alg == PSA_ALG_TLS12_ECJPAKE_TO_PMS) {
+ operation->capacity = PSA_HASH_LENGTH(PSA_ALG_SHA_256);
+ return PSA_SUCCESS;
+ }
+#endif
+#if defined(PSA_WANT_ALG_PBKDF2_AES_CMAC_PRF_128)
+ if (kdf_alg == PSA_ALG_PBKDF2_AES_CMAC_PRF_128) {
+#if (SIZE_MAX > UINT32_MAX)
+ operation->capacity = UINT32_MAX * (size_t) PSA_MAC_LENGTH(
+ PSA_KEY_TYPE_AES,
+ 128U,
+ PSA_ALG_CMAC);
+#else
+ operation->capacity = SIZE_MAX;
+#endif
+ return PSA_SUCCESS;
+ }
+#endif /* PSA_WANT_ALG_PBKDF2_AES_CMAC_PRF_128 */
+
+ /* After this point, if kdf_alg is not valid then value of hash_alg may be
+ * invalid or meaningless but it does not affect this function */
+ psa_algorithm_t hash_alg = PSA_ALG_GET_HASH(kdf_alg);
+ size_t hash_size = PSA_HASH_LENGTH(hash_alg);
+ if (hash_size == 0) {
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+
+ /* Make sure that hash_alg is a supported hash algorithm. Otherwise
+ * we might fail later, which is somewhat unfriendly and potentially
+ * risk-prone. */
+ psa_status_t status = psa_hash_try_support(hash_alg);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+#if defined(PSA_WANT_ALG_HKDF)
+ if (PSA_ALG_IS_HKDF(kdf_alg)) {
+ operation->capacity = 255 * hash_size;
+ } else
+#endif
+#if defined(PSA_WANT_ALG_HKDF_EXTRACT)
+ if (PSA_ALG_IS_HKDF_EXTRACT(kdf_alg)) {
+ operation->capacity = hash_size;
+ } else
+#endif
+#if defined(PSA_WANT_ALG_HKDF_EXPAND)
+ if (PSA_ALG_IS_HKDF_EXPAND(kdf_alg)) {
+ operation->capacity = 255 * hash_size;
+ } else
+#endif
+#if defined(PSA_WANT_ALG_TLS12_PRF)
+ if (PSA_ALG_IS_TLS12_PRF(kdf_alg) &&
+ (hash_alg == PSA_ALG_SHA_256 || hash_alg == PSA_ALG_SHA_384)) {
+ operation->capacity = SIZE_MAX;
+ } else
+#endif
+#if defined(PSA_WANT_ALG_TLS12_PSK_TO_MS)
+ if (PSA_ALG_IS_TLS12_PSK_TO_MS(kdf_alg) &&
+ (hash_alg == PSA_ALG_SHA_256 || hash_alg == PSA_ALG_SHA_384)) {
+ /* Master Secret is always 48 bytes
+ * https://datatracker.ietf.org/doc/html/rfc5246.html#section-8.1 */
+ operation->capacity = 48U;
+ } else
+#endif
+#if defined(PSA_WANT_ALG_PBKDF2_HMAC)
+ if (PSA_ALG_IS_PBKDF2_HMAC(kdf_alg)) {
+#if (SIZE_MAX > UINT32_MAX)
+ operation->capacity = UINT32_MAX * hash_size;
+#else
+ operation->capacity = SIZE_MAX;
+#endif
+ } else
+#endif /* PSA_WANT_ALG_PBKDF2_HMAC */
+ {
+ (void) hash_size;
+ status = PSA_ERROR_NOT_SUPPORTED;
+ }
+ return status;
+}
+
+static psa_status_t psa_key_derivation_setup_kdf(
+ psa_key_derivation_operation_t *operation,
+ psa_algorithm_t kdf_alg)
+{
+ /* Make sure that operation->ctx is properly zero-initialised. (Macro
+ * initialisers for this union leave some bytes unspecified.) */
+ memset(&operation->ctx, 0, sizeof(operation->ctx));
+
+ /* Make sure that kdf_alg is a supported key derivation algorithm. */
+ if (!is_kdf_alg_supported(kdf_alg)) {
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+
+ psa_status_t status = psa_key_derivation_set_maximum_capacity(operation,
+ kdf_alg);
+ return status;
+}
+
+static psa_status_t psa_key_agreement_try_support(psa_algorithm_t alg)
+{
+#if defined(PSA_WANT_ALG_ECDH)
+ if (alg == PSA_ALG_ECDH) {
+ return PSA_SUCCESS;
+ }
+#endif
+#if defined(PSA_WANT_ALG_FFDH)
+ if (alg == PSA_ALG_FFDH) {
+ return PSA_SUCCESS;
+ }
+#endif
+ (void) alg;
+ return PSA_ERROR_NOT_SUPPORTED;
+}
+
+static int psa_key_derivation_allows_free_form_secret_input(
+ psa_algorithm_t kdf_alg)
+{
+#if defined(PSA_WANT_ALG_TLS12_ECJPAKE_TO_PMS)
+ if (kdf_alg == PSA_ALG_TLS12_ECJPAKE_TO_PMS) {
+ return 0;
+ }
+#endif
+ (void) kdf_alg;
+ return 1;
+}
+#endif /* AT_LEAST_ONE_BUILTIN_KDF */
+
+psa_status_t psa_key_derivation_setup(psa_key_derivation_operation_t *operation,
+ psa_algorithm_t alg)
+{
+ psa_status_t status;
+
+ if (operation->alg != 0) {
+ return PSA_ERROR_BAD_STATE;
+ }
+
+ if (PSA_ALG_IS_RAW_KEY_AGREEMENT(alg)) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ } else if (PSA_ALG_IS_KEY_AGREEMENT(alg)) {
+#if defined(AT_LEAST_ONE_BUILTIN_KDF)
+ psa_algorithm_t kdf_alg = PSA_ALG_KEY_AGREEMENT_GET_KDF(alg);
+ psa_algorithm_t ka_alg = PSA_ALG_KEY_AGREEMENT_GET_BASE(alg);
+ status = psa_key_agreement_try_support(ka_alg);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+ if (!psa_key_derivation_allows_free_form_secret_input(kdf_alg)) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+ status = psa_key_derivation_setup_kdf(operation, kdf_alg);
+#else
+ return PSA_ERROR_NOT_SUPPORTED;
+#endif /* AT_LEAST_ONE_BUILTIN_KDF */
+ } else if (PSA_ALG_IS_KEY_DERIVATION(alg)) {
+#if defined(AT_LEAST_ONE_BUILTIN_KDF)
+ status = psa_key_derivation_setup_kdf(operation, alg);
+#else
+ return PSA_ERROR_NOT_SUPPORTED;
+#endif /* AT_LEAST_ONE_BUILTIN_KDF */
+ } else {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ if (status == PSA_SUCCESS) {
+ operation->alg = alg;
+ }
+ return status;
+}
+
+#if defined(BUILTIN_ALG_ANY_HKDF)
+static psa_status_t psa_hkdf_input(psa_hkdf_key_derivation_t *hkdf,
+ psa_algorithm_t kdf_alg,
+ psa_key_derivation_step_t step,
+ const uint8_t *data,
+ size_t data_length)
+{
+ psa_algorithm_t hash_alg = PSA_ALG_HKDF_GET_HASH(kdf_alg);
+ psa_status_t status;
+ switch (step) {
+ case PSA_KEY_DERIVATION_INPUT_SALT:
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXPAND)
+ if (PSA_ALG_IS_HKDF_EXPAND(kdf_alg)) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXPAND */
+ if (hkdf->state != HKDF_STATE_INIT) {
+ return PSA_ERROR_BAD_STATE;
+ } else {
+ status = psa_key_derivation_start_hmac(&hkdf->hmac,
+ hash_alg,
+ data, data_length);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+ hkdf->state = HKDF_STATE_STARTED;
+ return PSA_SUCCESS;
+ }
+ case PSA_KEY_DERIVATION_INPUT_SECRET:
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXPAND)
+ if (PSA_ALG_IS_HKDF_EXPAND(kdf_alg)) {
+ /* We shouldn't be in different state as HKDF_EXPAND only allows
+ * two inputs: SECRET (this case) and INFO which does not modify
+ * the state. It could happen only if the hkdf
+ * object was corrupted. */
+ if (hkdf->state != HKDF_STATE_INIT) {
+ return PSA_ERROR_BAD_STATE;
+ }
+
+ /* Allow only input that fits expected prk size */
+ if (data_length != PSA_HASH_LENGTH(hash_alg)) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ memcpy(hkdf->prk, data, data_length);
+ } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXPAND */
+ {
+ /* HKDF: If no salt was provided, use an empty salt.
+ * HKDF-EXTRACT: salt is mandatory. */
+ if (hkdf->state == HKDF_STATE_INIT) {
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT)
+ if (PSA_ALG_IS_HKDF_EXTRACT(kdf_alg)) {
+ return PSA_ERROR_BAD_STATE;
+ }
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT */
+ status = psa_key_derivation_start_hmac(&hkdf->hmac,
+ hash_alg,
+ NULL, 0);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+ hkdf->state = HKDF_STATE_STARTED;
+ }
+ if (hkdf->state != HKDF_STATE_STARTED) {
+ return PSA_ERROR_BAD_STATE;
+ }
+ status = psa_mac_update(&hkdf->hmac,
+ data, data_length);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+ status = psa_mac_sign_finish(&hkdf->hmac,
+ hkdf->prk,
+ sizeof(hkdf->prk),
+ &data_length);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+ }
+
+ hkdf->state = HKDF_STATE_KEYED;
+ hkdf->block_number = 0;
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT)
+ if (PSA_ALG_IS_HKDF_EXTRACT(kdf_alg)) {
+ /* The only block of output is the PRK. */
+ memcpy(hkdf->output_block, hkdf->prk, PSA_HASH_LENGTH(hash_alg));
+ hkdf->offset_in_block = 0;
+ } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT */
+ {
+ /* Block 0 is empty, and the next block will be
+ * generated by psa_key_derivation_hkdf_read(). */
+ hkdf->offset_in_block = PSA_HASH_LENGTH(hash_alg);
+ }
+
+ return PSA_SUCCESS;
+ case PSA_KEY_DERIVATION_INPUT_INFO:
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT)
+ if (PSA_ALG_IS_HKDF_EXTRACT(kdf_alg)) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXPAND)
+ if (PSA_ALG_IS_HKDF_EXPAND(kdf_alg) &&
+ hkdf->state == HKDF_STATE_INIT) {
+ return PSA_ERROR_BAD_STATE;
+ }
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT */
+ if (hkdf->state == HKDF_STATE_OUTPUT) {
+ return PSA_ERROR_BAD_STATE;
+ }
+ if (hkdf->info_set) {
+ return PSA_ERROR_BAD_STATE;
+ }
+ hkdf->info_length = data_length;
+ if (data_length != 0) {
+ hkdf->info = mbedtls_calloc(1, data_length);
+ if (hkdf->info == NULL) {
+ return PSA_ERROR_INSUFFICIENT_MEMORY;
+ }
+ memcpy(hkdf->info, data, data_length);
+ }
+ hkdf->info_set = 1;
+ return PSA_SUCCESS;
+ default:
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+}
+#endif /* BUILTIN_ALG_ANY_HKDF */
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PRF) || \
+ defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS)
+static psa_status_t psa_tls12_prf_set_seed(psa_tls12_prf_key_derivation_t *prf,
+ const uint8_t *data,
+ size_t data_length)
+{
+ if (prf->state != PSA_TLS12_PRF_STATE_INIT) {
+ return PSA_ERROR_BAD_STATE;
+ }
+
+ if (data_length != 0) {
+ prf->seed = mbedtls_calloc(1, data_length);
+ if (prf->seed == NULL) {
+ return PSA_ERROR_INSUFFICIENT_MEMORY;
+ }
+
+ memcpy(prf->seed, data, data_length);
+ prf->seed_length = data_length;
+ }
+
+ prf->state = PSA_TLS12_PRF_STATE_SEED_SET;
+
+ return PSA_SUCCESS;
+}
+
+static psa_status_t psa_tls12_prf_set_key(psa_tls12_prf_key_derivation_t *prf,
+ const uint8_t *data,
+ size_t data_length)
+{
+ if (prf->state != PSA_TLS12_PRF_STATE_SEED_SET &&
+ prf->state != PSA_TLS12_PRF_STATE_OTHER_KEY_SET) {
+ return PSA_ERROR_BAD_STATE;
+ }
+
+ if (data_length != 0) {
+ prf->secret = mbedtls_calloc(1, data_length);
+ if (prf->secret == NULL) {
+ return PSA_ERROR_INSUFFICIENT_MEMORY;
+ }
+
+ memcpy(prf->secret, data, data_length);
+ prf->secret_length = data_length;
+ }
+
+ prf->state = PSA_TLS12_PRF_STATE_KEY_SET;
+
+ return PSA_SUCCESS;
+}
+
+static psa_status_t psa_tls12_prf_set_label(psa_tls12_prf_key_derivation_t *prf,
+ const uint8_t *data,
+ size_t data_length)
+{
+ if (prf->state != PSA_TLS12_PRF_STATE_KEY_SET) {
+ return PSA_ERROR_BAD_STATE;
+ }
+
+ if (data_length != 0) {
+ prf->label = mbedtls_calloc(1, data_length);
+ if (prf->label == NULL) {
+ return PSA_ERROR_INSUFFICIENT_MEMORY;
+ }
+
+ memcpy(prf->label, data, data_length);
+ prf->label_length = data_length;
+ }
+
+ prf->state = PSA_TLS12_PRF_STATE_LABEL_SET;
+
+ return PSA_SUCCESS;
+}
+
+static psa_status_t psa_tls12_prf_input(psa_tls12_prf_key_derivation_t *prf,
+ psa_key_derivation_step_t step,
+ const uint8_t *data,
+ size_t data_length)
+{
+ switch (step) {
+ case PSA_KEY_DERIVATION_INPUT_SEED:
+ return psa_tls12_prf_set_seed(prf, data, data_length);
+ case PSA_KEY_DERIVATION_INPUT_SECRET:
+ return psa_tls12_prf_set_key(prf, data, data_length);
+ case PSA_KEY_DERIVATION_INPUT_LABEL:
+ return psa_tls12_prf_set_label(prf, data, data_length);
+ default:
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+}
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_TLS12_PRF) ||
+ * MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS */
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS)
+static psa_status_t psa_tls12_prf_psk_to_ms_set_key(
+ psa_tls12_prf_key_derivation_t *prf,
+ const uint8_t *data,
+ size_t data_length)
+{
+ psa_status_t status;
+ const size_t pms_len = (prf->state == PSA_TLS12_PRF_STATE_OTHER_KEY_SET ?
+ 4 + data_length + prf->other_secret_length :
+ 4 + 2 * data_length);
+
+ if (data_length > PSA_TLS12_PSK_TO_MS_PSK_MAX_SIZE) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ uint8_t *pms = mbedtls_calloc(1, pms_len);
+ if (pms == NULL) {
+ return PSA_ERROR_INSUFFICIENT_MEMORY;
+ }
+ uint8_t *cur = pms;
+
+ /* pure-PSK:
+ * Quoting RFC 4279, Section 2:
+ *
+ * The premaster secret is formed as follows: if the PSK is N octets
+ * long, concatenate a uint16 with the value N, N zero octets, a second
+ * uint16 with the value N, and the PSK itself.
+ *
+ * mixed-PSK:
+ * In a DHE-PSK, RSA-PSK, ECDHE-PSK the premaster secret is formed as
+ * follows: concatenate a uint16 with the length of the other secret,
+ * the other secret itself, uint16 with the length of PSK, and the
+ * PSK itself.
+ * For details please check:
+ * - RFC 4279, Section 4 for the definition of RSA-PSK,
+ * - RFC 4279, Section 3 for the definition of DHE-PSK,
+ * - RFC 5489 for the definition of ECDHE-PSK.
+ */
+
+ if (prf->state == PSA_TLS12_PRF_STATE_OTHER_KEY_SET) {
+ *cur++ = MBEDTLS_BYTE_1(prf->other_secret_length);
+ *cur++ = MBEDTLS_BYTE_0(prf->other_secret_length);
+ if (prf->other_secret_length != 0) {
+ memcpy(cur, prf->other_secret, prf->other_secret_length);
+ mbedtls_platform_zeroize(prf->other_secret, prf->other_secret_length);
+ cur += prf->other_secret_length;
+ }
+ } else {
+ *cur++ = MBEDTLS_BYTE_1(data_length);
+ *cur++ = MBEDTLS_BYTE_0(data_length);
+ memset(cur, 0, data_length);
+ cur += data_length;
+ }
+
+ *cur++ = MBEDTLS_BYTE_1(data_length);
+ *cur++ = MBEDTLS_BYTE_0(data_length);
+ memcpy(cur, data, data_length);
+ cur += data_length;
+
+ status = psa_tls12_prf_set_key(prf, pms, (size_t) (cur - pms));
+
+ mbedtls_zeroize_and_free(pms, pms_len);
+ return status;
+}
+
+static psa_status_t psa_tls12_prf_psk_to_ms_set_other_key(
+ psa_tls12_prf_key_derivation_t *prf,
+ const uint8_t *data,
+ size_t data_length)
+{
+ if (prf->state != PSA_TLS12_PRF_STATE_SEED_SET) {
+ return PSA_ERROR_BAD_STATE;
+ }
+
+ if (data_length != 0) {
+ prf->other_secret = mbedtls_calloc(1, data_length);
+ if (prf->other_secret == NULL) {
+ return PSA_ERROR_INSUFFICIENT_MEMORY;
+ }
+
+ memcpy(prf->other_secret, data, data_length);
+ prf->other_secret_length = data_length;
+ } else {
+ prf->other_secret_length = 0;
+ }
+
+ prf->state = PSA_TLS12_PRF_STATE_OTHER_KEY_SET;
+
+ return PSA_SUCCESS;
+}
+
+static psa_status_t psa_tls12_prf_psk_to_ms_input(
+ psa_tls12_prf_key_derivation_t *prf,
+ psa_key_derivation_step_t step,
+ const uint8_t *data,
+ size_t data_length)
+{
+ switch (step) {
+ case PSA_KEY_DERIVATION_INPUT_SECRET:
+ return psa_tls12_prf_psk_to_ms_set_key(prf,
+ data, data_length);
+ break;
+ case PSA_KEY_DERIVATION_INPUT_OTHER_SECRET:
+ return psa_tls12_prf_psk_to_ms_set_other_key(prf,
+ data,
+ data_length);
+ break;
+ default:
+ return psa_tls12_prf_input(prf, step, data, data_length);
+ break;
+
+ }
+}
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS */
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_ECJPAKE_TO_PMS)
+static psa_status_t psa_tls12_ecjpake_to_pms_input(
+ psa_tls12_ecjpake_to_pms_t *ecjpake,
+ psa_key_derivation_step_t step,
+ const uint8_t *data,
+ size_t data_length)
+{
+ if (data_length != PSA_TLS12_ECJPAKE_TO_PMS_INPUT_SIZE ||
+ step != PSA_KEY_DERIVATION_INPUT_SECRET) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ /* Check if the passed point is in an uncompressed form */
+ if (data[0] != 0x04) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ /* Only K.X has to be extracted - bytes 1 to 32 inclusive. */
+ memcpy(ecjpake->data, data + 1, PSA_TLS12_ECJPAKE_TO_PMS_DATA_SIZE);
+
+ return PSA_SUCCESS;
+}
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_TLS12_ECJPAKE_TO_PMS */
+
+#if defined(PSA_HAVE_SOFT_PBKDF2)
+static psa_status_t psa_pbkdf2_set_input_cost(
+ psa_pbkdf2_key_derivation_t *pbkdf2,
+ psa_key_derivation_step_t step,
+ uint64_t data)
+{
+ if (step != PSA_KEY_DERIVATION_INPUT_COST) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ if (pbkdf2->state != PSA_PBKDF2_STATE_INIT) {
+ return PSA_ERROR_BAD_STATE;
+ }
+
+ if (data > PSA_VENDOR_PBKDF2_MAX_ITERATIONS) {
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+
+ if (data == 0) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ pbkdf2->input_cost = data;
+ pbkdf2->state = PSA_PBKDF2_STATE_INPUT_COST_SET;
+
+ return PSA_SUCCESS;
+}
+
+static psa_status_t psa_pbkdf2_set_salt(psa_pbkdf2_key_derivation_t *pbkdf2,
+ const uint8_t *data,
+ size_t data_length)
+{
+ if (pbkdf2->state == PSA_PBKDF2_STATE_INPUT_COST_SET) {
+ pbkdf2->state = PSA_PBKDF2_STATE_SALT_SET;
+ } else if (pbkdf2->state == PSA_PBKDF2_STATE_SALT_SET) {
+ /* Appending to existing salt. No state change. */
+ } else {
+ return PSA_ERROR_BAD_STATE;
+ }
+
+ if (data_length == 0) {
+ /* Appending an empty string, nothing to do. */
+ } else {
+ uint8_t *next_salt;
+
+ next_salt = mbedtls_calloc(1, data_length + pbkdf2->salt_length);
+ if (next_salt == NULL) {
+ return PSA_ERROR_INSUFFICIENT_MEMORY;
+ }
+
+ if (pbkdf2->salt_length != 0) {
+ memcpy(next_salt, pbkdf2->salt, pbkdf2->salt_length);
+ }
+ memcpy(next_salt + pbkdf2->salt_length, data, data_length);
+ pbkdf2->salt_length += data_length;
+ mbedtls_free(pbkdf2->salt);
+ pbkdf2->salt = next_salt;
+ }
+ return PSA_SUCCESS;
+}
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_PBKDF2_HMAC)
+static psa_status_t psa_pbkdf2_hmac_set_password(psa_algorithm_t hash_alg,
+ const uint8_t *input,
+ size_t input_len,
+ uint8_t *output,
+ size_t *output_len)
+{
+ psa_status_t status = PSA_SUCCESS;
+ if (input_len > PSA_HASH_BLOCK_LENGTH(hash_alg)) {
+ return psa_hash_compute(hash_alg, input, input_len, output,
+ PSA_HMAC_MAX_HASH_BLOCK_SIZE, output_len);
+ } else if (input_len > 0) {
+ memcpy(output, input, input_len);
+ }
+ *output_len = PSA_HASH_BLOCK_LENGTH(hash_alg);
+ return status;
+}
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_PBKDF2_HMAC */
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_PBKDF2_AES_CMAC_PRF_128)
+static psa_status_t psa_pbkdf2_cmac_set_password(const uint8_t *input,
+ size_t input_len,
+ uint8_t *output,
+ size_t *output_len)
+{
+ psa_status_t status = PSA_SUCCESS;
+ if (input_len != PSA_MAC_LENGTH(PSA_KEY_TYPE_AES, 128U, PSA_ALG_CMAC)) {
+ psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
+ uint8_t zeros[16] = { 0 };
+ psa_set_key_type(&attributes, PSA_KEY_TYPE_AES);
+ psa_set_key_bits(&attributes, PSA_BYTES_TO_BITS(sizeof(zeros)));
+ psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_SIGN_MESSAGE);
+ /* Passing PSA_MAC_LENGTH(PSA_KEY_TYPE_AES, 128U, PSA_ALG_CMAC) as
+ * mac_size as the driver function sets mac_output_length = mac_size
+ * on success. See https://github.com/Mbed-TLS/mbedtls/issues/7801 */
+ status = psa_driver_wrapper_mac_compute(&attributes,
+ zeros, sizeof(zeros),
+ PSA_ALG_CMAC, input, input_len,
+ output,
+ PSA_MAC_LENGTH(PSA_KEY_TYPE_AES,
+ 128U,
+ PSA_ALG_CMAC),
+ output_len);
+ } else {
+ memcpy(output, input, input_len);
+ *output_len = PSA_MAC_LENGTH(PSA_KEY_TYPE_AES, 128U, PSA_ALG_CMAC);
+ }
+ return status;
+}
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_PBKDF2_AES_CMAC_PRF_128 */
+
+static psa_status_t psa_pbkdf2_set_password(psa_pbkdf2_key_derivation_t *pbkdf2,
+ psa_algorithm_t kdf_alg,
+ const uint8_t *data,
+ size_t data_length)
+{
+ psa_status_t status = PSA_SUCCESS;
+ if (pbkdf2->state != PSA_PBKDF2_STATE_SALT_SET) {
+ return PSA_ERROR_BAD_STATE;
+ }
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_PBKDF2_HMAC)
+ if (PSA_ALG_IS_PBKDF2_HMAC(kdf_alg)) {
+ psa_algorithm_t hash_alg = PSA_ALG_PBKDF2_HMAC_GET_HASH(kdf_alg);
+ status = psa_pbkdf2_hmac_set_password(hash_alg, data, data_length,
+ pbkdf2->password,
+ &pbkdf2->password_length);
+ } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_PBKDF2_HMAC */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_PBKDF2_AES_CMAC_PRF_128)
+ if (kdf_alg == PSA_ALG_PBKDF2_AES_CMAC_PRF_128) {
+ status = psa_pbkdf2_cmac_set_password(data, data_length,
+ pbkdf2->password,
+ &pbkdf2->password_length);
+ } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_PBKDF2_AES_CMAC_PRF_128 */
+ {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ pbkdf2->state = PSA_PBKDF2_STATE_PASSWORD_SET;
+
+ return status;
+}
+
+static psa_status_t psa_pbkdf2_input(psa_pbkdf2_key_derivation_t *pbkdf2,
+ psa_algorithm_t kdf_alg,
+ psa_key_derivation_step_t step,
+ const uint8_t *data,
+ size_t data_length)
+{
+ switch (step) {
+ case PSA_KEY_DERIVATION_INPUT_SALT:
+ return psa_pbkdf2_set_salt(pbkdf2, data, data_length);
+ case PSA_KEY_DERIVATION_INPUT_PASSWORD:
+ return psa_pbkdf2_set_password(pbkdf2, kdf_alg, data, data_length);
+ default:
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+}
+#endif /* PSA_HAVE_SOFT_PBKDF2 */
+
+/** Check whether the given key type is acceptable for the given
+ * input step of a key derivation.
+ *
+ * Secret inputs must have the type #PSA_KEY_TYPE_DERIVE.
+ * Non-secret inputs must have the type #PSA_KEY_TYPE_RAW_DATA.
+ * Both secret and non-secret inputs can alternatively have the type
+ * #PSA_KEY_TYPE_NONE, which is never the type of a key object, meaning
+ * that the input was passed as a buffer rather than via a key object.
+ */
+static int psa_key_derivation_check_input_type(
+ psa_key_derivation_step_t step,
+ psa_key_type_t key_type)
+{
+ switch (step) {
+ case PSA_KEY_DERIVATION_INPUT_SECRET:
+ if (key_type == PSA_KEY_TYPE_DERIVE) {
+ return PSA_SUCCESS;
+ }
+ if (key_type == PSA_KEY_TYPE_NONE) {
+ return PSA_SUCCESS;
+ }
+ break;
+ case PSA_KEY_DERIVATION_INPUT_OTHER_SECRET:
+ if (key_type == PSA_KEY_TYPE_DERIVE) {
+ return PSA_SUCCESS;
+ }
+ if (key_type == PSA_KEY_TYPE_NONE) {
+ return PSA_SUCCESS;
+ }
+ break;
+ case PSA_KEY_DERIVATION_INPUT_LABEL:
+ case PSA_KEY_DERIVATION_INPUT_SALT:
+ case PSA_KEY_DERIVATION_INPUT_INFO:
+ case PSA_KEY_DERIVATION_INPUT_SEED:
+ if (key_type == PSA_KEY_TYPE_RAW_DATA) {
+ return PSA_SUCCESS;
+ }
+ if (key_type == PSA_KEY_TYPE_NONE) {
+ return PSA_SUCCESS;
+ }
+ break;
+ case PSA_KEY_DERIVATION_INPUT_PASSWORD:
+ if (key_type == PSA_KEY_TYPE_PASSWORD) {
+ return PSA_SUCCESS;
+ }
+ if (key_type == PSA_KEY_TYPE_DERIVE) {
+ return PSA_SUCCESS;
+ }
+ if (key_type == PSA_KEY_TYPE_NONE) {
+ return PSA_SUCCESS;
+ }
+ break;
+ }
+ return PSA_ERROR_INVALID_ARGUMENT;
+}
+
+static psa_status_t psa_key_derivation_input_internal(
+ psa_key_derivation_operation_t *operation,
+ psa_key_derivation_step_t step,
+ psa_key_type_t key_type,
+ const uint8_t *data,
+ size_t data_length)
+{
+ psa_status_t status;
+ psa_algorithm_t kdf_alg = psa_key_derivation_get_kdf_alg(operation);
+
+ status = psa_key_derivation_check_input_type(step, key_type);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+#if defined(BUILTIN_ALG_ANY_HKDF)
+ if (PSA_ALG_IS_ANY_HKDF(kdf_alg)) {
+ status = psa_hkdf_input(&operation->ctx.hkdf, kdf_alg,
+ step, data, data_length);
+ } else
+#endif /* BUILTIN_ALG_ANY_HKDF */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PRF)
+ if (PSA_ALG_IS_TLS12_PRF(kdf_alg)) {
+ status = psa_tls12_prf_input(&operation->ctx.tls12_prf,
+ step, data, data_length);
+ } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_TLS12_PRF */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS)
+ if (PSA_ALG_IS_TLS12_PSK_TO_MS(kdf_alg)) {
+ status = psa_tls12_prf_psk_to_ms_input(&operation->ctx.tls12_prf,
+ step, data, data_length);
+ } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_TLS12_PSK_TO_MS */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_TLS12_ECJPAKE_TO_PMS)
+ if (kdf_alg == PSA_ALG_TLS12_ECJPAKE_TO_PMS) {
+ status = psa_tls12_ecjpake_to_pms_input(
+ &operation->ctx.tls12_ecjpake_to_pms, step, data, data_length);
+ } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_TLS12_ECJPAKE_TO_PMS */
+#if defined(PSA_HAVE_SOFT_PBKDF2)
+ if (PSA_ALG_IS_PBKDF2(kdf_alg)) {
+ status = psa_pbkdf2_input(&operation->ctx.pbkdf2, kdf_alg,
+ step, data, data_length);
+ } else
+#endif /* PSA_HAVE_SOFT_PBKDF2 */
+ {
+ /* This can't happen unless the operation object was not initialized */
+ (void) data;
+ (void) data_length;
+ (void) kdf_alg;
+ return PSA_ERROR_BAD_STATE;
+ }
+
+exit:
+ if (status != PSA_SUCCESS) {
+ psa_key_derivation_abort(operation);
+ }
+ return status;
+}
+
+static psa_status_t psa_key_derivation_input_integer_internal(
+ psa_key_derivation_operation_t *operation,
+ psa_key_derivation_step_t step,
+ uint64_t value)
+{
+ psa_status_t status;
+ psa_algorithm_t kdf_alg = psa_key_derivation_get_kdf_alg(operation);
+
+#if defined(PSA_HAVE_SOFT_PBKDF2)
+ if (PSA_ALG_IS_PBKDF2(kdf_alg)) {
+ status = psa_pbkdf2_set_input_cost(
+ &operation->ctx.pbkdf2, step, value);
+ } else
+#endif /* PSA_HAVE_SOFT_PBKDF2 */
+ {
+ (void) step;
+ (void) value;
+ (void) kdf_alg;
+ status = PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ if (status != PSA_SUCCESS) {
+ psa_key_derivation_abort(operation);
+ }
+ return status;
+}
+
+psa_status_t psa_key_derivation_input_bytes(
+ psa_key_derivation_operation_t *operation,
+ psa_key_derivation_step_t step,
+ const uint8_t *data_external,
+ size_t data_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ LOCAL_INPUT_DECLARE(data_external, data);
+
+ LOCAL_INPUT_ALLOC(data_external, data_length, data);
+
+ status = psa_key_derivation_input_internal(operation, step,
+ PSA_KEY_TYPE_NONE,
+ data, data_length);
+#if !defined(MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS)
+exit:
+#endif
+ LOCAL_INPUT_FREE(data_external, data);
+ return status;
+}
+
+psa_status_t psa_key_derivation_input_integer(
+ psa_key_derivation_operation_t *operation,
+ psa_key_derivation_step_t step,
+ uint64_t value)
+{
+ return psa_key_derivation_input_integer_internal(operation, step, value);
+}
+
+psa_status_t psa_key_derivation_input_key(
+ psa_key_derivation_operation_t *operation,
+ psa_key_derivation_step_t step,
+ mbedtls_svc_key_id_t key)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_key_slot_t *slot;
+
+ status = psa_get_and_lock_transparent_key_slot_with_policy(
+ key, &slot, PSA_KEY_USAGE_DERIVE, operation->alg);
+ if (status != PSA_SUCCESS) {
+ psa_key_derivation_abort(operation);
+ return status;
+ }
+
+ /* Passing a key object as a SECRET or PASSWORD input unlocks the
+ * permission to output to a key object. */
+ if (step == PSA_KEY_DERIVATION_INPUT_SECRET ||
+ step == PSA_KEY_DERIVATION_INPUT_PASSWORD) {
+ operation->can_output_key = 1;
+ }
+
+ status = psa_key_derivation_input_internal(operation,
+ step, slot->attr.type,
+ slot->key.data,
+ slot->key.bytes);
+
+ unlock_status = psa_unregister_read_under_mutex(slot);
+
+ return (status == PSA_SUCCESS) ? unlock_status : status;
+}
+
+
+
+/****************************************************************/
+/* Key agreement */
+/****************************************************************/
+
+psa_status_t psa_key_agreement_raw_builtin(const psa_key_attributes_t *attributes,
+ const uint8_t *key_buffer,
+ size_t key_buffer_size,
+ psa_algorithm_t alg,
+ const uint8_t *peer_key,
+ size_t peer_key_length,
+ uint8_t *shared_secret,
+ size_t shared_secret_size,
+ size_t *shared_secret_length)
+{
+ switch (alg) {
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_ECDH)
+ case PSA_ALG_ECDH:
+ return mbedtls_psa_key_agreement_ecdh(attributes, key_buffer,
+ key_buffer_size, alg,
+ peer_key, peer_key_length,
+ shared_secret,
+ shared_secret_size,
+ shared_secret_length);
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_ECDH */
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_FFDH)
+ case PSA_ALG_FFDH:
+ return mbedtls_psa_ffdh_key_agreement(attributes,
+ peer_key,
+ peer_key_length,
+ key_buffer,
+ key_buffer_size,
+ shared_secret,
+ shared_secret_size,
+ shared_secret_length);
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_FFDH */
+
+ default:
+ (void) attributes;
+ (void) key_buffer;
+ (void) key_buffer_size;
+ (void) peer_key;
+ (void) peer_key_length;
+ (void) shared_secret;
+ (void) shared_secret_size;
+ (void) shared_secret_length;
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+}
+
+/** Internal function for raw key agreement
+ * Calls the driver wrapper which will hand off key agreement task
+ * to the driver's implementation if a driver is present.
+ * Fallback specified in the driver wrapper is built-in raw key agreement
+ * (psa_key_agreement_raw_builtin).
+ */
+static psa_status_t psa_key_agreement_raw_internal(psa_algorithm_t alg,
+ psa_key_slot_t *private_key,
+ const uint8_t *peer_key,
+ size_t peer_key_length,
+ uint8_t *shared_secret,
+ size_t shared_secret_size,
+ size_t *shared_secret_length)
+{
+ if (!PSA_ALG_IS_RAW_KEY_AGREEMENT(alg)) {
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+
+ return psa_driver_wrapper_key_agreement(&private_key->attr,
+ private_key->key.data,
+ private_key->key.bytes, alg,
+ peer_key, peer_key_length,
+ shared_secret,
+ shared_secret_size,
+ shared_secret_length);
+}
+
+/* Note that if this function fails, you must call psa_key_derivation_abort()
+ * to potentially free embedded data structures and wipe confidential data.
+ */
+static psa_status_t psa_key_agreement_internal(psa_key_derivation_operation_t *operation,
+ psa_key_derivation_step_t step,
+ psa_key_slot_t *private_key,
+ const uint8_t *peer_key,
+ size_t peer_key_length)
+{
+ psa_status_t status;
+ uint8_t shared_secret[PSA_RAW_KEY_AGREEMENT_OUTPUT_MAX_SIZE] = { 0 };
+ size_t shared_secret_length = 0;
+ psa_algorithm_t ka_alg = PSA_ALG_KEY_AGREEMENT_GET_BASE(operation->alg);
+
+ /* Step 1: run the secret agreement algorithm to generate the shared
+ * secret. */
+ status = psa_key_agreement_raw_internal(ka_alg,
+ private_key,
+ peer_key, peer_key_length,
+ shared_secret,
+ sizeof(shared_secret),
+ &shared_secret_length);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ /* Step 2: set up the key derivation to generate key material from
+ * the shared secret. A shared secret is permitted wherever a key
+ * of type DERIVE is permitted. */
+ status = psa_key_derivation_input_internal(operation, step,
+ PSA_KEY_TYPE_DERIVE,
+ shared_secret,
+ shared_secret_length);
+exit:
+ mbedtls_platform_zeroize(shared_secret, shared_secret_length);
+ return status;
+}
+
+psa_status_t psa_key_derivation_key_agreement(psa_key_derivation_operation_t *operation,
+ psa_key_derivation_step_t step,
+ mbedtls_svc_key_id_t private_key,
+ const uint8_t *peer_key_external,
+ size_t peer_key_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_key_slot_t *slot;
+ LOCAL_INPUT_DECLARE(peer_key_external, peer_key);
+
+ if (!PSA_ALG_IS_KEY_AGREEMENT(operation->alg)) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+ status = psa_get_and_lock_transparent_key_slot_with_policy(
+ private_key, &slot, PSA_KEY_USAGE_DERIVE, operation->alg);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ LOCAL_INPUT_ALLOC(peer_key_external, peer_key_length, peer_key);
+ status = psa_key_agreement_internal(operation, step,
+ slot,
+ peer_key, peer_key_length);
+
+#if !defined(MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS)
+exit:
+#endif
+ if (status != PSA_SUCCESS) {
+ psa_key_derivation_abort(operation);
+ } else {
+ /* If a private key has been added as SECRET, we allow the derived
+ * key material to be used as a key in PSA Crypto. */
+ if (step == PSA_KEY_DERIVATION_INPUT_SECRET) {
+ operation->can_output_key = 1;
+ }
+ }
+
+ unlock_status = psa_unregister_read_under_mutex(slot);
+ LOCAL_INPUT_FREE(peer_key_external, peer_key);
+
+ return (status == PSA_SUCCESS) ? unlock_status : status;
+}
+
+psa_status_t psa_raw_key_agreement(psa_algorithm_t alg,
+ mbedtls_svc_key_id_t private_key,
+ const uint8_t *peer_key_external,
+ size_t peer_key_length,
+ uint8_t *output_external,
+ size_t output_size,
+ size_t *output_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_key_slot_t *slot = NULL;
+ size_t expected_length;
+ LOCAL_INPUT_DECLARE(peer_key_external, peer_key);
+ LOCAL_OUTPUT_DECLARE(output_external, output);
+ LOCAL_OUTPUT_ALLOC(output_external, output_size, output);
+
+ if (!PSA_ALG_IS_KEY_AGREEMENT(alg)) {
+ status = PSA_ERROR_INVALID_ARGUMENT;
+ goto exit;
+ }
+ status = psa_get_and_lock_transparent_key_slot_with_policy(
+ private_key, &slot, PSA_KEY_USAGE_DERIVE, alg);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ /* PSA_RAW_KEY_AGREEMENT_OUTPUT_SIZE() is in general an upper bound
+ * for the output size. The PSA specification only guarantees that this
+ * function works if output_size >= PSA_RAW_KEY_AGREEMENT_OUTPUT_SIZE(...),
+ * but it might be nice to allow smaller buffers if the output fits.
+ * At the time of writing this comment, with only ECDH implemented,
+ * PSA_RAW_KEY_AGREEMENT_OUTPUT_SIZE() is exact so the point is moot.
+ * If FFDH is implemented, PSA_RAW_KEY_AGREEMENT_OUTPUT_SIZE() can easily
+ * be exact for it as well. */
+ expected_length =
+ PSA_RAW_KEY_AGREEMENT_OUTPUT_SIZE(slot->attr.type, slot->attr.bits);
+ if (output_size < expected_length) {
+ status = PSA_ERROR_BUFFER_TOO_SMALL;
+ goto exit;
+ }
+
+ LOCAL_INPUT_ALLOC(peer_key_external, peer_key_length, peer_key);
+ status = psa_key_agreement_raw_internal(alg, slot,
+ peer_key, peer_key_length,
+ output, output_size,
+ output_length);
+
+exit:
+ /* Check for successful allocation of output,
+ * with an unsuccessful status. */
+ if (output != NULL && status != PSA_SUCCESS) {
+ /* If an error happens and is not handled properly, the output
+ * may be used as a key to protect sensitive data. Arrange for such
+ * a key to be random, which is likely to result in decryption or
+ * verification errors. This is better than filling the buffer with
+ * some constant data such as zeros, which would result in the data
+ * being protected with a reproducible, easily knowable key.
+ */
+ psa_generate_random_internal(output, output_size);
+ *output_length = output_size;
+ }
+
+ if (output == NULL) {
+ /* output allocation failed. */
+ *output_length = 0;
+ }
+
+ unlock_status = psa_unregister_read_under_mutex(slot);
+
+ LOCAL_INPUT_FREE(peer_key_external, peer_key);
+ LOCAL_OUTPUT_FREE(output_external, output);
+ return (status == PSA_SUCCESS) ? unlock_status : status;
+}
+
+
+/****************************************************************/
+/* Random generation */
+/****************************************************************/
+
+#if defined(MBEDTLS_PSA_INJECT_ENTROPY)
+#include "entropy_poll.h"
+#endif
+
+/** Initialize the PSA random generator.
+ *
+ * Note: the mbedtls_threading_psa_rngdata_mutex should be held when calling
+ * this function if mutexes are enabled.
+ */
+static void mbedtls_psa_random_init(mbedtls_psa_random_context_t *rng)
+{
+#if defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG)
+ memset(rng, 0, sizeof(*rng));
+#else /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */
+
+ /* Set default configuration if
+ * mbedtls_psa_crypto_configure_entropy_sources() hasn't been called. */
+ if (rng->entropy_init == NULL) {
+ rng->entropy_init = mbedtls_entropy_init;
+ }
+ if (rng->entropy_free == NULL) {
+ rng->entropy_free = mbedtls_entropy_free;
+ }
+
+ rng->entropy_init(&rng->entropy);
+#if defined(MBEDTLS_PSA_INJECT_ENTROPY) && \
+ defined(MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES)
+ /* The PSA entropy injection feature depends on using NV seed as an entropy
+ * source. Add NV seed as an entropy source for PSA entropy injection. */
+ mbedtls_entropy_add_source(&rng->entropy,
+ mbedtls_nv_seed_poll, NULL,
+ MBEDTLS_ENTROPY_BLOCK_SIZE,
+ MBEDTLS_ENTROPY_SOURCE_STRONG);
+#endif
+
+ mbedtls_psa_drbg_init(&rng->drbg);
+#endif /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */
+}
+
+/** Deinitialize the PSA random generator.
+ *
+ * Note: the mbedtls_threading_psa_rngdata_mutex should be held when calling
+ * this function if mutexes are enabled.
+ */
+static void mbedtls_psa_random_free(mbedtls_psa_random_context_t *rng)
+{
+#if defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG)
+ memset(rng, 0, sizeof(*rng));
+#else /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */
+ mbedtls_psa_drbg_free(&rng->drbg);
+ rng->entropy_free(&rng->entropy);
+#endif /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */
+}
+
+/** Seed the PSA random generator.
+ */
+static psa_status_t mbedtls_psa_random_seed(mbedtls_psa_random_context_t *rng)
+{
+#if defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG)
+ /* Do nothing: the external RNG seeds itself. */
+ (void) rng;
+ return PSA_SUCCESS;
+#else /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */
+ const unsigned char drbg_seed[] = "PSA";
+ int ret = mbedtls_psa_drbg_seed(&rng->drbg, &rng->entropy,
+ drbg_seed, sizeof(drbg_seed) - 1);
+ return mbedtls_to_psa_error(ret);
+#endif /* MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG */
+}
+
+psa_status_t psa_generate_random(uint8_t *output_external,
+ size_t output_size)
+{
+ psa_status_t status;
+
+ LOCAL_OUTPUT_DECLARE(output_external, output);
+ LOCAL_OUTPUT_ALLOC(output_external, output_size, output);
+
+ status = psa_generate_random_internal(output, output_size);
+
+#if !defined(MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS)
+exit:
+#endif
+ LOCAL_OUTPUT_FREE(output_external, output);
+ return status;
+}
+
+#if defined(MBEDTLS_PSA_INJECT_ENTROPY)
+psa_status_t mbedtls_psa_inject_entropy(const uint8_t *seed,
+ size_t seed_size)
+{
+ if (psa_get_initialized()) {
+ return PSA_ERROR_NOT_PERMITTED;
+ }
+
+ if (((seed_size < MBEDTLS_ENTROPY_MIN_PLATFORM) ||
+ (seed_size < MBEDTLS_ENTROPY_BLOCK_SIZE)) ||
+ (seed_size > MBEDTLS_ENTROPY_MAX_SEED_SIZE)) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ return mbedtls_psa_storage_inject_entropy(seed, seed_size);
+}
+#endif /* MBEDTLS_PSA_INJECT_ENTROPY */
+
+/** Validate the key type and size for key generation
+ *
+ * \param type The key type
+ * \param bits The number of bits of the key
+ *
+ * \retval #PSA_SUCCESS
+ * The key type and size are valid.
+ * \retval #PSA_ERROR_INVALID_ARGUMENT
+ * The size in bits of the key is not valid.
+ * \retval #PSA_ERROR_NOT_SUPPORTED
+ * The type and/or the size in bits of the key or the combination of
+ * the two is not supported.
+ */
+static psa_status_t psa_validate_key_type_and_size_for_key_generation(
+ psa_key_type_t type, size_t bits)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+ if (key_type_is_raw_bytes(type)) {
+ status = psa_validate_unstructured_key_bit_size(type, bits);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+ } else
+#if defined(PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_GENERATE)
+ if (PSA_KEY_TYPE_IS_RSA(type) && PSA_KEY_TYPE_IS_KEY_PAIR(type)) {
+ if (bits > PSA_VENDOR_RSA_MAX_KEY_BITS) {
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+ if (bits < PSA_VENDOR_RSA_GENERATE_MIN_KEY_BITS) {
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+
+ /* Accept only byte-aligned keys, for the same reasons as
+ * in psa_import_rsa_key(). */
+ if (bits % 8 != 0) {
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+ } else
+#endif /* defined(PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_GENERATE) */
+
+#if defined(PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_GENERATE)
+ if (PSA_KEY_TYPE_IS_ECC(type) && PSA_KEY_TYPE_IS_KEY_PAIR(type)) {
+ /* To avoid empty block, return successfully here. */
+ return PSA_SUCCESS;
+ } else
+#endif /* defined(PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_GENERATE) */
+
+#if defined(PSA_WANT_KEY_TYPE_DH_KEY_PAIR_GENERATE)
+ if (PSA_KEY_TYPE_IS_DH(type) && PSA_KEY_TYPE_IS_KEY_PAIR(type)) {
+ if (psa_is_dh_key_size_valid(bits) == 0) {
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+ } else
+#endif /* defined(PSA_WANT_KEY_TYPE_DH_KEY_PAIR_GENERATE) */
+ {
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+
+ return PSA_SUCCESS;
+}
+
+psa_status_t psa_generate_key_internal(
+ const psa_key_attributes_t *attributes,
+ const psa_custom_key_parameters_t *custom,
+ const uint8_t *custom_data,
+ size_t custom_data_length,
+ uint8_t *key_buffer, size_t key_buffer_size, size_t *key_buffer_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_key_type_t type = attributes->type;
+
+ /* Only used for RSA */
+ (void) custom;
+ (void) custom_data;
+ (void) custom_data_length;
+
+ if (key_type_is_raw_bytes(type)) {
+ status = psa_generate_random_internal(key_buffer, key_buffer_size);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DES)
+ if (type == PSA_KEY_TYPE_DES) {
+ psa_des_set_key_parity(key_buffer, key_buffer_size);
+ }
+#endif /* MBEDTLS_PSA_BUILTIN_KEY_TYPE_DES */
+ } else
+
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR_GENERATE)
+ if (type == PSA_KEY_TYPE_RSA_KEY_PAIR) {
+ return mbedtls_psa_rsa_generate_key(attributes,
+ custom_data, custom_data_length,
+ key_buffer,
+ key_buffer_size,
+ key_buffer_length);
+ } else
+#endif /* defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR_GENERATE) */
+
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR_GENERATE)
+ if (PSA_KEY_TYPE_IS_ECC(type) && PSA_KEY_TYPE_IS_KEY_PAIR(type)) {
+ return mbedtls_psa_ecp_generate_key(attributes,
+ key_buffer,
+ key_buffer_size,
+ key_buffer_length);
+ } else
+#endif /* defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR_GENERATE) */
+
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_KEY_PAIR_GENERATE)
+ if (PSA_KEY_TYPE_IS_DH(type) && PSA_KEY_TYPE_IS_KEY_PAIR(type)) {
+ return mbedtls_psa_ffdh_generate_key(attributes,
+ key_buffer,
+ key_buffer_size,
+ key_buffer_length);
+ } else
+#endif /* defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_KEY_PAIR_GENERATE) */
+ {
+ (void) key_buffer_length;
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+
+ return PSA_SUCCESS;
+}
+
+psa_status_t psa_generate_key_custom(const psa_key_attributes_t *attributes,
+ const psa_custom_key_parameters_t *custom,
+ const uint8_t *custom_data,
+ size_t custom_data_length,
+ mbedtls_svc_key_id_t *key)
+{
+ psa_status_t status;
+ psa_key_slot_t *slot = NULL;
+ psa_se_drv_table_entry_t *driver = NULL;
+ size_t key_buffer_size;
+
+ *key = MBEDTLS_SVC_KEY_ID_INIT;
+
+ /* Reject any attempt to create a zero-length key so that we don't
+ * risk tripping up later, e.g. on a malloc(0) that returns NULL. */
+ if (psa_get_key_bits(attributes) == 0) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ /* Reject any attempt to create a public key. */
+ if (PSA_KEY_TYPE_IS_PUBLIC_KEY(attributes->type)) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+#if defined(PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_GENERATE)
+ if (attributes->type == PSA_KEY_TYPE_RSA_KEY_PAIR) {
+ if (custom->flags != 0) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+ } else
+#endif
+ if (!psa_custom_key_parameters_are_default(custom, custom_data_length)) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ status = psa_start_key_creation(PSA_KEY_CREATION_GENERATE, attributes,
+ &slot, &driver);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ /* In the case of a transparent key or an opaque key stored in local
+ * storage ( thus not in the case of generating a key in a secure element
+ * with storage ( MBEDTLS_PSA_CRYPTO_SE_C ) ),we have to allocate a
+ * buffer to hold the generated key material. */
+ if (slot->key.data == NULL) {
+ if (PSA_KEY_LIFETIME_GET_LOCATION(attributes->lifetime) ==
+ PSA_KEY_LOCATION_LOCAL_STORAGE) {
+ status = psa_validate_key_type_and_size_for_key_generation(
+ attributes->type, attributes->bits);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ key_buffer_size = PSA_EXPORT_KEY_OUTPUT_SIZE(
+ attributes->type,
+ attributes->bits);
+ } else {
+ status = psa_driver_wrapper_get_key_buffer_size(
+ attributes, &key_buffer_size);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+ }
+
+ status = psa_allocate_buffer_to_slot(slot, key_buffer_size);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+ }
+
+ status = psa_driver_wrapper_generate_key(attributes,
+ custom,
+ custom_data, custom_data_length,
+ slot->key.data, slot->key.bytes,
+ &slot->key.bytes);
+ if (status != PSA_SUCCESS) {
+ psa_remove_key_data_from_memory(slot);
+ }
+
+exit:
+ if (status == PSA_SUCCESS) {
+ status = psa_finish_key_creation(slot, driver, key);
+ }
+ if (status != PSA_SUCCESS) {
+ psa_fail_key_creation(slot, driver);
+ }
+
+ return status;
+}
+
+psa_status_t psa_generate_key_ext(const psa_key_attributes_t *attributes,
+ const psa_key_production_parameters_t *params,
+ size_t params_data_length,
+ mbedtls_svc_key_id_t *key)
+{
+ return psa_generate_key_custom(
+ attributes,
+ (const psa_custom_key_parameters_t *) params,
+ params->data, params_data_length,
+ key);
+}
+
+psa_status_t psa_generate_key(const psa_key_attributes_t *attributes,
+ mbedtls_svc_key_id_t *key)
+{
+ return psa_generate_key_custom(attributes,
+ &default_custom_production,
+ NULL, 0,
+ key);
+}
+
+/****************************************************************/
+/* Module setup */
+/****************************************************************/
+
+#if !defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG)
+psa_status_t mbedtls_psa_crypto_configure_entropy_sources(
+ void (* entropy_init)(mbedtls_entropy_context *ctx),
+ void (* entropy_free)(mbedtls_entropy_context *ctx))
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+#if defined(MBEDTLS_THREADING_C)
+ mbedtls_mutex_lock(&mbedtls_threading_psa_rngdata_mutex);
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+ if (global_data.rng_state != RNG_NOT_INITIALIZED) {
+ status = PSA_ERROR_BAD_STATE;
+ } else {
+ global_data.rng.entropy_init = entropy_init;
+ global_data.rng.entropy_free = entropy_free;
+ status = PSA_SUCCESS;
+ }
+
+#if defined(MBEDTLS_THREADING_C)
+ mbedtls_mutex_unlock(&mbedtls_threading_psa_rngdata_mutex);
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+ return status;
+}
+#endif /* !defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG) */
+
+void mbedtls_psa_crypto_free(void)
+{
+
+#if defined(MBEDTLS_THREADING_C)
+ mbedtls_mutex_lock(&mbedtls_threading_psa_globaldata_mutex);
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+ /* Nothing to do to free transaction. */
+ if (global_data.initialized & PSA_CRYPTO_SUBSYSTEM_TRANSACTION_INITIALIZED) {
+ global_data.initialized &= ~PSA_CRYPTO_SUBSYSTEM_TRANSACTION_INITIALIZED;
+ }
+
+ if (global_data.initialized & PSA_CRYPTO_SUBSYSTEM_KEY_SLOTS_INITIALIZED) {
+ psa_wipe_all_key_slots();
+ global_data.initialized &= ~PSA_CRYPTO_SUBSYSTEM_KEY_SLOTS_INITIALIZED;
+ }
+
+#if defined(MBEDTLS_THREADING_C)
+ mbedtls_mutex_unlock(&mbedtls_threading_psa_globaldata_mutex);
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+#if defined(MBEDTLS_THREADING_C)
+ mbedtls_mutex_lock(&mbedtls_threading_psa_rngdata_mutex);
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+ if (global_data.rng_state != RNG_NOT_INITIALIZED) {
+ mbedtls_psa_random_free(&global_data.rng);
+ }
+ global_data.rng_state = RNG_NOT_INITIALIZED;
+ mbedtls_platform_zeroize(&global_data.rng, sizeof(global_data.rng));
+
+#if defined(MBEDTLS_THREADING_C)
+ mbedtls_mutex_unlock(&mbedtls_threading_psa_rngdata_mutex);
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+#if defined(MBEDTLS_THREADING_C)
+ mbedtls_mutex_lock(&mbedtls_threading_psa_globaldata_mutex);
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+ /* Terminate drivers */
+ if (global_data.initialized & PSA_CRYPTO_SUBSYSTEM_DRIVER_WRAPPERS_INITIALIZED) {
+ psa_driver_wrapper_free();
+ global_data.initialized &= ~PSA_CRYPTO_SUBSYSTEM_DRIVER_WRAPPERS_INITIALIZED;
+ }
+
+#if defined(MBEDTLS_THREADING_C)
+ mbedtls_mutex_unlock(&mbedtls_threading_psa_globaldata_mutex);
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+}
+
+#if defined(PSA_CRYPTO_STORAGE_HAS_TRANSACTIONS)
+/** Recover a transaction that was interrupted by a power failure.
+ *
+ * This function is called during initialization, before psa_crypto_init()
+ * returns. If this function returns a failure status, the initialization
+ * fails.
+ */
+static psa_status_t psa_crypto_recover_transaction(
+ const psa_crypto_transaction_t *transaction)
+{
+ switch (transaction->unknown.type) {
+ case PSA_CRYPTO_TRANSACTION_CREATE_KEY:
+ case PSA_CRYPTO_TRANSACTION_DESTROY_KEY:
+ /* TODO - fall through to the failure case until this
+ * is implemented.
+ * https://github.com/ARMmbed/mbed-crypto/issues/218
+ */
+ default:
+ /* We found an unsupported transaction in the storage.
+ * We don't know what state the storage is in. Give up. */
+ return PSA_ERROR_DATA_INVALID;
+ }
+}
+#endif /* PSA_CRYPTO_STORAGE_HAS_TRANSACTIONS */
+
+static psa_status_t mbedtls_psa_crypto_init_subsystem(mbedtls_psa_crypto_subsystem subsystem)
+{
+ psa_status_t status = PSA_SUCCESS;
+ uint8_t driver_wrappers_initialized = 0;
+
+ switch (subsystem) {
+ case PSA_CRYPTO_SUBSYSTEM_DRIVER_WRAPPERS:
+
+#if defined(MBEDTLS_THREADING_C)
+ PSA_THREADING_CHK_GOTO_EXIT(mbedtls_mutex_lock(&mbedtls_threading_psa_globaldata_mutex));
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+ if (!(global_data.initialized & PSA_CRYPTO_SUBSYSTEM_DRIVER_WRAPPERS_INITIALIZED)) {
+ /* Init drivers */
+ status = psa_driver_wrapper_init();
+
+ /* Drivers need shutdown regardless of startup errors. */
+ global_data.initialized |= PSA_CRYPTO_SUBSYSTEM_DRIVER_WRAPPERS_INITIALIZED;
+
+
+ }
+#if defined(MBEDTLS_THREADING_C)
+ PSA_THREADING_CHK_GOTO_EXIT(mbedtls_mutex_unlock(
+ &mbedtls_threading_psa_globaldata_mutex));
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+ break;
+
+ case PSA_CRYPTO_SUBSYSTEM_KEY_SLOTS:
+
+#if defined(MBEDTLS_THREADING_C)
+ PSA_THREADING_CHK_GOTO_EXIT(mbedtls_mutex_lock(&mbedtls_threading_psa_globaldata_mutex));
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+ if (!(global_data.initialized & PSA_CRYPTO_SUBSYSTEM_KEY_SLOTS_INITIALIZED)) {
+ status = psa_initialize_key_slots();
+
+ /* Need to wipe keys even if initialization fails. */
+ global_data.initialized |= PSA_CRYPTO_SUBSYSTEM_KEY_SLOTS_INITIALIZED;
+
+ }
+#if defined(MBEDTLS_THREADING_C)
+ PSA_THREADING_CHK_GOTO_EXIT(mbedtls_mutex_unlock(
+ &mbedtls_threading_psa_globaldata_mutex));
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+ break;
+
+ case PSA_CRYPTO_SUBSYSTEM_RNG:
+
+#if defined(MBEDTLS_THREADING_C)
+ PSA_THREADING_CHK_GOTO_EXIT(mbedtls_mutex_lock(&mbedtls_threading_psa_globaldata_mutex));
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+ driver_wrappers_initialized =
+ (global_data.initialized & PSA_CRYPTO_SUBSYSTEM_DRIVER_WRAPPERS_INITIALIZED);
+
+#if defined(MBEDTLS_THREADING_C)
+ PSA_THREADING_CHK_GOTO_EXIT(mbedtls_mutex_unlock(
+ &mbedtls_threading_psa_globaldata_mutex));
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+ /* Need to use separate mutex here, as initialisation can require
+ * testing of init flags, which requires locking the global data
+ * mutex. */
+#if defined(MBEDTLS_THREADING_C)
+ PSA_THREADING_CHK_GOTO_EXIT(mbedtls_mutex_lock(&mbedtls_threading_psa_rngdata_mutex));
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+ /* Initialize and seed the random generator. */
+ if (global_data.rng_state == RNG_NOT_INITIALIZED && driver_wrappers_initialized) {
+ mbedtls_psa_random_init(&global_data.rng);
+ global_data.rng_state = RNG_INITIALIZED;
+
+ status = mbedtls_psa_random_seed(&global_data.rng);
+ if (status == PSA_SUCCESS) {
+ global_data.rng_state = RNG_SEEDED;
+ }
+ }
+
+#if defined(MBEDTLS_THREADING_C)
+ PSA_THREADING_CHK_GOTO_EXIT(mbedtls_mutex_unlock(
+ &mbedtls_threading_psa_rngdata_mutex));
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+ break;
+
+ case PSA_CRYPTO_SUBSYSTEM_TRANSACTION:
+
+#if defined(MBEDTLS_THREADING_C)
+ PSA_THREADING_CHK_GOTO_EXIT(mbedtls_mutex_lock(&mbedtls_threading_psa_globaldata_mutex));
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+ if (!(global_data.initialized & PSA_CRYPTO_SUBSYSTEM_TRANSACTION_INITIALIZED)) {
+#if defined(PSA_CRYPTO_STORAGE_HAS_TRANSACTIONS)
+ status = psa_crypto_load_transaction();
+ if (status == PSA_SUCCESS) {
+ status = psa_crypto_recover_transaction(&psa_crypto_transaction);
+ if (status == PSA_SUCCESS) {
+ global_data.initialized |= PSA_CRYPTO_SUBSYSTEM_TRANSACTION_INITIALIZED;
+ }
+ status = psa_crypto_stop_transaction();
+ } else if (status == PSA_ERROR_DOES_NOT_EXIST) {
+ /* There's no transaction to complete. It's all good. */
+ global_data.initialized |= PSA_CRYPTO_SUBSYSTEM_TRANSACTION_INITIALIZED;
+ status = PSA_SUCCESS;
+ }
+#else /* defined(PSA_CRYPTO_STORAGE_HAS_TRANSACTIONS) */
+ global_data.initialized |= PSA_CRYPTO_SUBSYSTEM_TRANSACTION_INITIALIZED;
+ status = PSA_SUCCESS;
+#endif /* defined(PSA_CRYPTO_STORAGE_HAS_TRANSACTIONS) */
+ }
+
+#if defined(MBEDTLS_THREADING_C)
+ PSA_THREADING_CHK_GOTO_EXIT(mbedtls_mutex_unlock(
+ &mbedtls_threading_psa_globaldata_mutex));
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+ break;
+
+ default:
+ status = PSA_ERROR_CORRUPTION_DETECTED;
+ }
+
+ /* Exit label only required when using threading macros. */
+#if defined(MBEDTLS_THREADING_C)
+exit:
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+ return status;
+}
+
+psa_status_t psa_crypto_init(void)
+{
+ psa_status_t status;
+
+ /* Double initialization is explicitly allowed. Early out if everything is
+ * done. */
+ if (psa_get_initialized()) {
+ return PSA_SUCCESS;
+ }
+
+ status = mbedtls_psa_crypto_init_subsystem(PSA_CRYPTO_SUBSYSTEM_DRIVER_WRAPPERS);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ status = mbedtls_psa_crypto_init_subsystem(PSA_CRYPTO_SUBSYSTEM_KEY_SLOTS);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ status = mbedtls_psa_crypto_init_subsystem(PSA_CRYPTO_SUBSYSTEM_RNG);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ status = mbedtls_psa_crypto_init_subsystem(PSA_CRYPTO_SUBSYSTEM_TRANSACTION);
+
+exit:
+
+ if (status != PSA_SUCCESS) {
+ mbedtls_psa_crypto_free();
+ }
+
+ return status;
+}
+
+#if defined(PSA_WANT_ALG_SOME_PAKE)
+psa_status_t psa_crypto_driver_pake_get_password_len(
+ const psa_crypto_driver_pake_inputs_t *inputs,
+ size_t *password_len)
+{
+ if (inputs->password_len == 0) {
+ return PSA_ERROR_BAD_STATE;
+ }
+
+ *password_len = inputs->password_len;
+
+ return PSA_SUCCESS;
+}
+
+psa_status_t psa_crypto_driver_pake_get_password(
+ const psa_crypto_driver_pake_inputs_t *inputs,
+ uint8_t *buffer, size_t buffer_size, size_t *buffer_length)
+{
+ if (inputs->password_len == 0) {
+ return PSA_ERROR_BAD_STATE;
+ }
+
+ if (buffer_size < inputs->password_len) {
+ return PSA_ERROR_BUFFER_TOO_SMALL;
+ }
+
+ memcpy(buffer, inputs->password, inputs->password_len);
+ *buffer_length = inputs->password_len;
+
+ return PSA_SUCCESS;
+}
+
+psa_status_t psa_crypto_driver_pake_get_user_len(
+ const psa_crypto_driver_pake_inputs_t *inputs,
+ size_t *user_len)
+{
+ if (inputs->user_len == 0) {
+ return PSA_ERROR_BAD_STATE;
+ }
+
+ *user_len = inputs->user_len;
+
+ return PSA_SUCCESS;
+}
+
+psa_status_t psa_crypto_driver_pake_get_user(
+ const psa_crypto_driver_pake_inputs_t *inputs,
+ uint8_t *user_id, size_t user_id_size, size_t *user_id_len)
+{
+ if (inputs->user_len == 0) {
+ return PSA_ERROR_BAD_STATE;
+ }
+
+ if (user_id_size < inputs->user_len) {
+ return PSA_ERROR_BUFFER_TOO_SMALL;
+ }
+
+ memcpy(user_id, inputs->user, inputs->user_len);
+ *user_id_len = inputs->user_len;
+
+ return PSA_SUCCESS;
+}
+
+psa_status_t psa_crypto_driver_pake_get_peer_len(
+ const psa_crypto_driver_pake_inputs_t *inputs,
+ size_t *peer_len)
+{
+ if (inputs->peer_len == 0) {
+ return PSA_ERROR_BAD_STATE;
+ }
+
+ *peer_len = inputs->peer_len;
+
+ return PSA_SUCCESS;
+}
+
+psa_status_t psa_crypto_driver_pake_get_peer(
+ const psa_crypto_driver_pake_inputs_t *inputs,
+ uint8_t *peer_id, size_t peer_id_size, size_t *peer_id_length)
+{
+ if (inputs->peer_len == 0) {
+ return PSA_ERROR_BAD_STATE;
+ }
+
+ if (peer_id_size < inputs->peer_len) {
+ return PSA_ERROR_BUFFER_TOO_SMALL;
+ }
+
+ memcpy(peer_id, inputs->peer, inputs->peer_len);
+ *peer_id_length = inputs->peer_len;
+
+ return PSA_SUCCESS;
+}
+
+psa_status_t psa_crypto_driver_pake_get_cipher_suite(
+ const psa_crypto_driver_pake_inputs_t *inputs,
+ psa_pake_cipher_suite_t *cipher_suite)
+{
+ if (inputs->cipher_suite.algorithm == PSA_ALG_NONE) {
+ return PSA_ERROR_BAD_STATE;
+ }
+
+ *cipher_suite = inputs->cipher_suite;
+
+ return PSA_SUCCESS;
+}
+
+psa_status_t psa_pake_setup(
+ psa_pake_operation_t *operation,
+ const psa_pake_cipher_suite_t *cipher_suite)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+ if (operation->stage != PSA_PAKE_OPERATION_STAGE_SETUP) {
+ status = PSA_ERROR_BAD_STATE;
+ goto exit;
+ }
+
+ if (PSA_ALG_IS_PAKE(cipher_suite->algorithm) == 0 ||
+ PSA_ALG_IS_HASH(cipher_suite->hash) == 0) {
+ status = PSA_ERROR_INVALID_ARGUMENT;
+ goto exit;
+ }
+
+ memset(&operation->data.inputs, 0, sizeof(operation->data.inputs));
+
+ operation->alg = cipher_suite->algorithm;
+ operation->primitive = PSA_PAKE_PRIMITIVE(cipher_suite->type,
+ cipher_suite->family, cipher_suite->bits);
+ operation->data.inputs.cipher_suite = *cipher_suite;
+
+#if defined(PSA_WANT_ALG_JPAKE)
+ if (operation->alg == PSA_ALG_JPAKE) {
+ psa_jpake_computation_stage_t *computation_stage =
+ &operation->computation_stage.jpake;
+
+ memset(computation_stage, 0, sizeof(*computation_stage));
+ computation_stage->step = PSA_PAKE_STEP_KEY_SHARE;
+ } else
+#endif /* PSA_WANT_ALG_JPAKE */
+ {
+ status = PSA_ERROR_NOT_SUPPORTED;
+ goto exit;
+ }
+
+ operation->stage = PSA_PAKE_OPERATION_STAGE_COLLECT_INPUTS;
+
+ return PSA_SUCCESS;
+exit:
+ psa_pake_abort(operation);
+ return status;
+}
+
+psa_status_t psa_pake_set_password_key(
+ psa_pake_operation_t *operation,
+ mbedtls_svc_key_id_t password)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_key_slot_t *slot = NULL;
+ psa_key_type_t type;
+
+ if (operation->stage != PSA_PAKE_OPERATION_STAGE_COLLECT_INPUTS) {
+ status = PSA_ERROR_BAD_STATE;
+ goto exit;
+ }
+
+ status = psa_get_and_lock_key_slot_with_policy(password, &slot,
+ PSA_KEY_USAGE_DERIVE,
+ operation->alg);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ type = psa_get_key_type(&slot->attr);
+
+ if (type != PSA_KEY_TYPE_PASSWORD &&
+ type != PSA_KEY_TYPE_PASSWORD_HASH) {
+ status = PSA_ERROR_INVALID_ARGUMENT;
+ goto exit;
+ }
+
+ operation->data.inputs.password = mbedtls_calloc(1, slot->key.bytes);
+ if (operation->data.inputs.password == NULL) {
+ status = PSA_ERROR_INSUFFICIENT_MEMORY;
+ goto exit;
+ }
+
+ memcpy(operation->data.inputs.password, slot->key.data, slot->key.bytes);
+ operation->data.inputs.password_len = slot->key.bytes;
+ operation->data.inputs.attributes = slot->attr;
+
+exit:
+ if (status != PSA_SUCCESS) {
+ psa_pake_abort(operation);
+ }
+ unlock_status = psa_unregister_read_under_mutex(slot);
+ return (status == PSA_SUCCESS) ? unlock_status : status;
+}
+
+psa_status_t psa_pake_set_user(
+ psa_pake_operation_t *operation,
+ const uint8_t *user_id_external,
+ size_t user_id_len)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ LOCAL_INPUT_DECLARE(user_id_external, user_id);
+
+ if (operation->stage != PSA_PAKE_OPERATION_STAGE_COLLECT_INPUTS) {
+ status = PSA_ERROR_BAD_STATE;
+ goto exit;
+ }
+
+ if (user_id_len == 0) {
+ status = PSA_ERROR_INVALID_ARGUMENT;
+ goto exit;
+ }
+
+ if (operation->data.inputs.user_len != 0) {
+ status = PSA_ERROR_BAD_STATE;
+ goto exit;
+ }
+
+ operation->data.inputs.user = mbedtls_calloc(1, user_id_len);
+ if (operation->data.inputs.user == NULL) {
+ status = PSA_ERROR_INSUFFICIENT_MEMORY;
+ goto exit;
+ }
+
+ LOCAL_INPUT_ALLOC(user_id_external, user_id_len, user_id);
+
+ memcpy(operation->data.inputs.user, user_id, user_id_len);
+ operation->data.inputs.user_len = user_id_len;
+
+ status = PSA_SUCCESS;
+
+exit:
+ LOCAL_INPUT_FREE(user_id_external, user_id);
+ if (status != PSA_SUCCESS) {
+ psa_pake_abort(operation);
+ }
+ return status;
+}
+
+psa_status_t psa_pake_set_peer(
+ psa_pake_operation_t *operation,
+ const uint8_t *peer_id_external,
+ size_t peer_id_len)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ LOCAL_INPUT_DECLARE(peer_id_external, peer_id);
+
+ if (operation->stage != PSA_PAKE_OPERATION_STAGE_COLLECT_INPUTS) {
+ status = PSA_ERROR_BAD_STATE;
+ goto exit;
+ }
+
+ if (peer_id_len == 0) {
+ status = PSA_ERROR_INVALID_ARGUMENT;
+ goto exit;
+ }
+
+ if (operation->data.inputs.peer_len != 0) {
+ status = PSA_ERROR_BAD_STATE;
+ goto exit;
+ }
+
+ operation->data.inputs.peer = mbedtls_calloc(1, peer_id_len);
+ if (operation->data.inputs.peer == NULL) {
+ status = PSA_ERROR_INSUFFICIENT_MEMORY;
+ goto exit;
+ }
+
+ LOCAL_INPUT_ALLOC(peer_id_external, peer_id_len, peer_id);
+
+ memcpy(operation->data.inputs.peer, peer_id, peer_id_len);
+ operation->data.inputs.peer_len = peer_id_len;
+
+ status = PSA_SUCCESS;
+
+exit:
+ LOCAL_INPUT_FREE(peer_id_external, peer_id);
+ if (status != PSA_SUCCESS) {
+ psa_pake_abort(operation);
+ }
+ return status;
+}
+
+psa_status_t psa_pake_set_role(
+ psa_pake_operation_t *operation,
+ psa_pake_role_t role)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+ if (operation->stage != PSA_PAKE_OPERATION_STAGE_COLLECT_INPUTS) {
+ status = PSA_ERROR_BAD_STATE;
+ goto exit;
+ }
+
+ switch (operation->alg) {
+#if defined(PSA_WANT_ALG_JPAKE)
+ case PSA_ALG_JPAKE:
+ if (role == PSA_PAKE_ROLE_NONE) {
+ return PSA_SUCCESS;
+ }
+ status = PSA_ERROR_INVALID_ARGUMENT;
+ break;
+#endif
+ default:
+ (void) role;
+ status = PSA_ERROR_NOT_SUPPORTED;
+ goto exit;
+ }
+exit:
+ psa_pake_abort(operation);
+ return status;
+}
+
+/* Auxiliary function to convert core computation stage to single driver step. */
+#if defined(PSA_WANT_ALG_JPAKE)
+static psa_crypto_driver_pake_step_t convert_jpake_computation_stage_to_driver_step(
+ psa_jpake_computation_stage_t *stage)
+{
+ psa_crypto_driver_pake_step_t key_share_step;
+ if (stage->round == PSA_JPAKE_FIRST) {
+ int is_x1;
+
+ if (stage->io_mode == PSA_JPAKE_OUTPUT) {
+ is_x1 = (stage->outputs < 1);
+ } else {
+ is_x1 = (stage->inputs < 1);
+ }
+
+ key_share_step = is_x1 ?
+ PSA_JPAKE_X1_STEP_KEY_SHARE :
+ PSA_JPAKE_X2_STEP_KEY_SHARE;
+ } else if (stage->round == PSA_JPAKE_SECOND) {
+ key_share_step = (stage->io_mode == PSA_JPAKE_OUTPUT) ?
+ PSA_JPAKE_X2S_STEP_KEY_SHARE :
+ PSA_JPAKE_X4S_STEP_KEY_SHARE;
+ } else {
+ return PSA_JPAKE_STEP_INVALID;
+ }
+ return (psa_crypto_driver_pake_step_t) (key_share_step + stage->step - PSA_PAKE_STEP_KEY_SHARE);
+}
+#endif /* PSA_WANT_ALG_JPAKE */
+
+static psa_status_t psa_pake_complete_inputs(
+ psa_pake_operation_t *operation)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ /* Create copy of the inputs on stack as inputs share memory
+ with the driver context which will be setup by the driver. */
+ psa_crypto_driver_pake_inputs_t inputs = operation->data.inputs;
+
+ if (inputs.password_len == 0) {
+ return PSA_ERROR_BAD_STATE;
+ }
+
+ if (operation->alg == PSA_ALG_JPAKE) {
+ if (inputs.user_len == 0 || inputs.peer_len == 0) {
+ return PSA_ERROR_BAD_STATE;
+ }
+ }
+
+ /* Clear driver context */
+ mbedtls_platform_zeroize(&operation->data, sizeof(operation->data));
+
+ status = psa_driver_wrapper_pake_setup(operation, &inputs);
+
+ /* Driver is responsible for creating its own copy of the password. */
+ mbedtls_zeroize_and_free(inputs.password, inputs.password_len);
+
+ /* User and peer are translated to role. */
+ mbedtls_free(inputs.user);
+ mbedtls_free(inputs.peer);
+
+ if (status == PSA_SUCCESS) {
+#if defined(PSA_WANT_ALG_JPAKE)
+ if (operation->alg == PSA_ALG_JPAKE) {
+ operation->stage = PSA_PAKE_OPERATION_STAGE_COMPUTATION;
+ } else
+#endif /* PSA_WANT_ALG_JPAKE */
+ {
+ status = PSA_ERROR_NOT_SUPPORTED;
+ }
+ }
+ return status;
+}
+
+#if defined(PSA_WANT_ALG_JPAKE)
+static psa_status_t psa_jpake_prologue(
+ psa_pake_operation_t *operation,
+ psa_pake_step_t step,
+ psa_jpake_io_mode_t io_mode)
+{
+ if (step != PSA_PAKE_STEP_KEY_SHARE &&
+ step != PSA_PAKE_STEP_ZK_PUBLIC &&
+ step != PSA_PAKE_STEP_ZK_PROOF) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ psa_jpake_computation_stage_t *computation_stage =
+ &operation->computation_stage.jpake;
+
+ if (computation_stage->round != PSA_JPAKE_FIRST &&
+ computation_stage->round != PSA_JPAKE_SECOND) {
+ return PSA_ERROR_BAD_STATE;
+ }
+
+ /* Check that the step we are given is the one we were expecting */
+ if (step != computation_stage->step) {
+ return PSA_ERROR_BAD_STATE;
+ }
+
+ if (step == PSA_PAKE_STEP_KEY_SHARE &&
+ computation_stage->inputs == 0 &&
+ computation_stage->outputs == 0) {
+ /* Start of the round, so function decides whether we are inputting
+ * or outputting */
+ computation_stage->io_mode = io_mode;
+ } else if (computation_stage->io_mode != io_mode) {
+ /* Middle of the round so the mode we are in must match the function
+ * called by the user */
+ return PSA_ERROR_BAD_STATE;
+ }
+
+ return PSA_SUCCESS;
+}
+
+static psa_status_t psa_jpake_epilogue(
+ psa_pake_operation_t *operation,
+ psa_jpake_io_mode_t io_mode)
+{
+ psa_jpake_computation_stage_t *stage =
+ &operation->computation_stage.jpake;
+
+ if (stage->step == PSA_PAKE_STEP_ZK_PROOF) {
+ /* End of an input/output */
+ if (io_mode == PSA_JPAKE_INPUT) {
+ stage->inputs++;
+ if (stage->inputs == PSA_JPAKE_EXPECTED_INPUTS(stage->round)) {
+ stage->io_mode = PSA_JPAKE_OUTPUT;
+ }
+ }
+ if (io_mode == PSA_JPAKE_OUTPUT) {
+ stage->outputs++;
+ if (stage->outputs == PSA_JPAKE_EXPECTED_OUTPUTS(stage->round)) {
+ stage->io_mode = PSA_JPAKE_INPUT;
+ }
+ }
+ if (stage->inputs == PSA_JPAKE_EXPECTED_INPUTS(stage->round) &&
+ stage->outputs == PSA_JPAKE_EXPECTED_OUTPUTS(stage->round)) {
+ /* End of a round, move to the next round */
+ stage->inputs = 0;
+ stage->outputs = 0;
+ stage->round++;
+ }
+ stage->step = PSA_PAKE_STEP_KEY_SHARE;
+ } else {
+ stage->step++;
+ }
+ return PSA_SUCCESS;
+}
+
+#endif /* PSA_WANT_ALG_JPAKE */
+
+psa_status_t psa_pake_output(
+ psa_pake_operation_t *operation,
+ psa_pake_step_t step,
+ uint8_t *output_external,
+ size_t output_size,
+ size_t *output_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_crypto_driver_pake_step_t driver_step = PSA_JPAKE_STEP_INVALID;
+ LOCAL_OUTPUT_DECLARE(output_external, output);
+ *output_length = 0;
+
+ if (operation->stage == PSA_PAKE_OPERATION_STAGE_COLLECT_INPUTS) {
+ status = psa_pake_complete_inputs(operation);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+ }
+
+ if (operation->stage != PSA_PAKE_OPERATION_STAGE_COMPUTATION) {
+ status = PSA_ERROR_BAD_STATE;
+ goto exit;
+ }
+
+ if (output_size == 0) {
+ status = PSA_ERROR_INVALID_ARGUMENT;
+ goto exit;
+ }
+
+ switch (operation->alg) {
+#if defined(PSA_WANT_ALG_JPAKE)
+ case PSA_ALG_JPAKE:
+ status = psa_jpake_prologue(operation, step, PSA_JPAKE_OUTPUT);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+ driver_step = convert_jpake_computation_stage_to_driver_step(
+ &operation->computation_stage.jpake);
+ break;
+#endif /* PSA_WANT_ALG_JPAKE */
+ default:
+ (void) step;
+ status = PSA_ERROR_NOT_SUPPORTED;
+ goto exit;
+ }
+
+ LOCAL_OUTPUT_ALLOC(output_external, output_size, output);
+
+ status = psa_driver_wrapper_pake_output(operation, driver_step,
+ output, output_size, output_length);
+
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ switch (operation->alg) {
+#if defined(PSA_WANT_ALG_JPAKE)
+ case PSA_ALG_JPAKE:
+ status = psa_jpake_epilogue(operation, PSA_JPAKE_OUTPUT);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+ break;
+#endif /* PSA_WANT_ALG_JPAKE */
+ default:
+ status = PSA_ERROR_NOT_SUPPORTED;
+ goto exit;
+ }
+
+exit:
+ LOCAL_OUTPUT_FREE(output_external, output);
+ if (status != PSA_SUCCESS) {
+ psa_pake_abort(operation);
+ }
+ return status;
+}
+
+psa_status_t psa_pake_input(
+ psa_pake_operation_t *operation,
+ psa_pake_step_t step,
+ const uint8_t *input_external,
+ size_t input_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_crypto_driver_pake_step_t driver_step = PSA_JPAKE_STEP_INVALID;
+ const size_t max_input_length = (size_t) PSA_PAKE_INPUT_SIZE(operation->alg,
+ operation->primitive,
+ step);
+ LOCAL_INPUT_DECLARE(input_external, input);
+
+ if (operation->stage == PSA_PAKE_OPERATION_STAGE_COLLECT_INPUTS) {
+ status = psa_pake_complete_inputs(operation);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+ }
+
+ if (operation->stage != PSA_PAKE_OPERATION_STAGE_COMPUTATION) {
+ status = PSA_ERROR_BAD_STATE;
+ goto exit;
+ }
+
+ if (input_length == 0 || input_length > max_input_length) {
+ status = PSA_ERROR_INVALID_ARGUMENT;
+ goto exit;
+ }
+
+ switch (operation->alg) {
+#if defined(PSA_WANT_ALG_JPAKE)
+ case PSA_ALG_JPAKE:
+ status = psa_jpake_prologue(operation, step, PSA_JPAKE_INPUT);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+ driver_step = convert_jpake_computation_stage_to_driver_step(
+ &operation->computation_stage.jpake);
+ break;
+#endif /* PSA_WANT_ALG_JPAKE */
+ default:
+ (void) step;
+ status = PSA_ERROR_NOT_SUPPORTED;
+ goto exit;
+ }
+
+ LOCAL_INPUT_ALLOC(input_external, input_length, input);
+ status = psa_driver_wrapper_pake_input(operation, driver_step,
+ input, input_length);
+
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ switch (operation->alg) {
+#if defined(PSA_WANT_ALG_JPAKE)
+ case PSA_ALG_JPAKE:
+ status = psa_jpake_epilogue(operation, PSA_JPAKE_INPUT);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+ break;
+#endif /* PSA_WANT_ALG_JPAKE */
+ default:
+ status = PSA_ERROR_NOT_SUPPORTED;
+ goto exit;
+ }
+
+exit:
+ LOCAL_INPUT_FREE(input_external, input);
+ if (status != PSA_SUCCESS) {
+ psa_pake_abort(operation);
+ }
+ return status;
+}
+
+psa_status_t psa_pake_get_implicit_key(
+ psa_pake_operation_t *operation,
+ psa_key_derivation_operation_t *output)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_status_t abort_status = PSA_ERROR_CORRUPTION_DETECTED;
+ uint8_t shared_key[MBEDTLS_PSA_JPAKE_BUFFER_SIZE];
+ size_t shared_key_len = 0;
+
+ if (operation->stage != PSA_PAKE_OPERATION_STAGE_COMPUTATION) {
+ status = PSA_ERROR_BAD_STATE;
+ goto exit;
+ }
+
+#if defined(PSA_WANT_ALG_JPAKE)
+ if (operation->alg == PSA_ALG_JPAKE) {
+ psa_jpake_computation_stage_t *computation_stage =
+ &operation->computation_stage.jpake;
+ if (computation_stage->round != PSA_JPAKE_FINISHED) {
+ status = PSA_ERROR_BAD_STATE;
+ goto exit;
+ }
+ } else
+#endif /* PSA_WANT_ALG_JPAKE */
+ {
+ status = PSA_ERROR_NOT_SUPPORTED;
+ goto exit;
+ }
+
+ status = psa_driver_wrapper_pake_get_implicit_key(operation,
+ shared_key,
+ sizeof(shared_key),
+ &shared_key_len);
+
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ status = psa_key_derivation_input_bytes(output,
+ PSA_KEY_DERIVATION_INPUT_SECRET,
+ shared_key,
+ shared_key_len);
+
+ mbedtls_platform_zeroize(shared_key, sizeof(shared_key));
+exit:
+ abort_status = psa_pake_abort(operation);
+ return status == PSA_SUCCESS ? abort_status : status;
+}
+
+psa_status_t psa_pake_abort(
+ psa_pake_operation_t *operation)
+{
+ psa_status_t status = PSA_SUCCESS;
+
+ if (operation->stage == PSA_PAKE_OPERATION_STAGE_COMPUTATION) {
+ status = psa_driver_wrapper_pake_abort(operation);
+ }
+
+ if (operation->stage == PSA_PAKE_OPERATION_STAGE_COLLECT_INPUTS) {
+ if (operation->data.inputs.password != NULL) {
+ mbedtls_zeroize_and_free(operation->data.inputs.password,
+ operation->data.inputs.password_len);
+ }
+ if (operation->data.inputs.user != NULL) {
+ mbedtls_free(operation->data.inputs.user);
+ }
+ if (operation->data.inputs.peer != NULL) {
+ mbedtls_free(operation->data.inputs.peer);
+ }
+ }
+ memset(operation, 0, sizeof(psa_pake_operation_t));
+
+ return status;
+}
+#endif /* PSA_WANT_ALG_SOME_PAKE */
+
+/* Memory copying test hooks. These are called before input copy, after input
+ * copy, before output copy and after output copy, respectively.
+ * They are used by memory-poisoning tests to temporarily unpoison buffers
+ * while they are copied. */
+#if defined(MBEDTLS_TEST_HOOKS)
+void (*psa_input_pre_copy_hook)(const uint8_t *input, size_t input_len) = NULL;
+void (*psa_input_post_copy_hook)(const uint8_t *input, size_t input_len) = NULL;
+void (*psa_output_pre_copy_hook)(const uint8_t *output, size_t output_len) = NULL;
+void (*psa_output_post_copy_hook)(const uint8_t *output, size_t output_len) = NULL;
+#endif
+
+/** Copy from an input buffer to a local copy.
+ *
+ * \param[in] input Pointer to input buffer.
+ * \param[in] input_len Length of the input buffer.
+ * \param[out] input_copy Pointer to a local copy in which to store the input data.
+ * \param[out] input_copy_len Length of the local copy buffer.
+ * \return #PSA_SUCCESS, if the buffer was successfully
+ * copied.
+ * \return #PSA_ERROR_CORRUPTION_DETECTED, if the local
+ * copy is too small to hold contents of the
+ * input buffer.
+ */
+MBEDTLS_STATIC_TESTABLE
+psa_status_t psa_crypto_copy_input(const uint8_t *input, size_t input_len,
+ uint8_t *input_copy, size_t input_copy_len)
+{
+ if (input_len > input_copy_len) {
+ return PSA_ERROR_CORRUPTION_DETECTED;
+ }
+
+#if defined(MBEDTLS_TEST_HOOKS)
+ if (psa_input_pre_copy_hook != NULL) {
+ psa_input_pre_copy_hook(input, input_len);
+ }
+#endif
+
+ if (input_len > 0) {
+ memcpy(input_copy, input, input_len);
+ }
+
+#if defined(MBEDTLS_TEST_HOOKS)
+ if (psa_input_post_copy_hook != NULL) {
+ psa_input_post_copy_hook(input, input_len);
+ }
+#endif
+
+ return PSA_SUCCESS;
+}
+
+/** Copy from a local output buffer into a user-supplied one.
+ *
+ * \param[in] output_copy Pointer to a local buffer containing the output.
+ * \param[in] output_copy_len Length of the local buffer.
+ * \param[out] output Pointer to user-supplied output buffer.
+ * \param[out] output_len Length of the user-supplied output buffer.
+ * \return #PSA_SUCCESS, if the buffer was successfully
+ * copied.
+ * \return #PSA_ERROR_BUFFER_TOO_SMALL, if the
+ * user-supplied output buffer is too small to
+ * hold the contents of the local buffer.
+ */
+MBEDTLS_STATIC_TESTABLE
+psa_status_t psa_crypto_copy_output(const uint8_t *output_copy, size_t output_copy_len,
+ uint8_t *output, size_t output_len)
+{
+ if (output_len < output_copy_len) {
+ return PSA_ERROR_BUFFER_TOO_SMALL;
+ }
+
+#if defined(MBEDTLS_TEST_HOOKS)
+ if (psa_output_pre_copy_hook != NULL) {
+ psa_output_pre_copy_hook(output, output_len);
+ }
+#endif
+
+ if (output_copy_len > 0) {
+ memcpy(output, output_copy, output_copy_len);
+ }
+
+#if defined(MBEDTLS_TEST_HOOKS)
+ if (psa_output_post_copy_hook != NULL) {
+ psa_output_post_copy_hook(output, output_len);
+ }
+#endif
+
+ return PSA_SUCCESS;
+}
+
+psa_status_t psa_crypto_local_input_alloc(const uint8_t *input, size_t input_len,
+ psa_crypto_local_input_t *local_input)
+{
+ psa_status_t status;
+
+ *local_input = PSA_CRYPTO_LOCAL_INPUT_INIT;
+
+ if (input_len == 0) {
+ return PSA_SUCCESS;
+ }
+
+ local_input->buffer = mbedtls_calloc(input_len, 1);
+ if (local_input->buffer == NULL) {
+ /* Since we dealt with the zero-length case above, we know that
+ * a NULL return value means a failure of allocation. */
+ return PSA_ERROR_INSUFFICIENT_MEMORY;
+ }
+ /* From now on, we must free local_input->buffer on error. */
+
+ local_input->length = input_len;
+
+ status = psa_crypto_copy_input(input, input_len,
+ local_input->buffer, local_input->length);
+ if (status != PSA_SUCCESS) {
+ goto error;
+ }
+
+ return PSA_SUCCESS;
+
+error:
+ mbedtls_free(local_input->buffer);
+ local_input->buffer = NULL;
+ local_input->length = 0;
+ return status;
+}
+
+void psa_crypto_local_input_free(psa_crypto_local_input_t *local_input)
+{
+ mbedtls_free(local_input->buffer);
+ local_input->buffer = NULL;
+ local_input->length = 0;
+}
+
+psa_status_t psa_crypto_local_output_alloc(uint8_t *output, size_t output_len,
+ psa_crypto_local_output_t *local_output)
+{
+ *local_output = PSA_CRYPTO_LOCAL_OUTPUT_INIT;
+
+ if (output_len == 0) {
+ return PSA_SUCCESS;
+ }
+ local_output->buffer = mbedtls_calloc(output_len, 1);
+ if (local_output->buffer == NULL) {
+ /* Since we dealt with the zero-length case above, we know that
+ * a NULL return value means a failure of allocation. */
+ return PSA_ERROR_INSUFFICIENT_MEMORY;
+ }
+ local_output->length = output_len;
+ local_output->original = output;
+
+ return PSA_SUCCESS;
+}
+
+psa_status_t psa_crypto_local_output_free(psa_crypto_local_output_t *local_output)
+{
+ psa_status_t status;
+
+ if (local_output->buffer == NULL) {
+ local_output->length = 0;
+ return PSA_SUCCESS;
+ }
+ if (local_output->original == NULL) {
+ /* We have an internal copy but nothing to copy back to. */
+ return PSA_ERROR_CORRUPTION_DETECTED;
+ }
+
+ status = psa_crypto_copy_output(local_output->buffer, local_output->length,
+ local_output->original, local_output->length);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ mbedtls_free(local_output->buffer);
+ local_output->buffer = NULL;
+ local_output->length = 0;
+
+ return PSA_SUCCESS;
+}
+
+#endif /* MBEDTLS_PSA_CRYPTO_C */
diff --git a/thirdparty/mbedtls/library/psa_crypto_aead.c b/thirdparty/mbedtls/library/psa_crypto_aead.c
new file mode 100644
index 0000000000..a201985b4f
--- /dev/null
+++ b/thirdparty/mbedtls/library/psa_crypto_aead.c
@@ -0,0 +1,649 @@
+/*
+ * PSA AEAD entry points
+ */
+/*
+ * Copyright The Mbed TLS Contributors
+ * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#include "common.h"
+
+#if defined(MBEDTLS_PSA_CRYPTO_C)
+
+#include "psa_crypto_aead.h"
+#include "psa_crypto_core.h"
+#include "psa_crypto_cipher.h"
+
+#include <string.h>
+#include "mbedtls/platform.h"
+
+#include "mbedtls/ccm.h"
+#include "mbedtls/chachapoly.h"
+#include "mbedtls/cipher.h"
+#include "mbedtls/gcm.h"
+#include "mbedtls/error.h"
+
+static psa_status_t psa_aead_setup(
+ mbedtls_psa_aead_operation_t *operation,
+ const psa_key_attributes_t *attributes,
+ const uint8_t *key_buffer,
+ size_t key_buffer_size,
+ psa_algorithm_t alg)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ mbedtls_cipher_id_t cipher_id;
+ mbedtls_cipher_mode_t mode;
+ size_t key_bits = attributes->bits;
+ (void) key_buffer_size;
+
+ status = mbedtls_cipher_values_from_psa(alg, attributes->type,
+ &key_bits, &mode, &cipher_id);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ switch (PSA_ALG_AEAD_WITH_SHORTENED_TAG(alg, 0)) {
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CCM)
+ case PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, 0):
+ operation->alg = PSA_ALG_CCM;
+ /* CCM allows the following tag lengths: 4, 6, 8, 10, 12, 14, 16.
+ * The call to mbedtls_ccm_encrypt_and_tag or
+ * mbedtls_ccm_auth_decrypt will validate the tag length. */
+ if (PSA_BLOCK_CIPHER_BLOCK_LENGTH(attributes->type) != 16) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ mbedtls_ccm_init(&operation->ctx.ccm);
+ status = mbedtls_to_psa_error(
+ mbedtls_ccm_setkey(&operation->ctx.ccm, cipher_id,
+ key_buffer, (unsigned int) key_bits));
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+ break;
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_CCM */
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_GCM)
+ case PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_GCM, 0):
+ operation->alg = PSA_ALG_GCM;
+ /* GCM allows the following tag lengths: 4, 8, 12, 13, 14, 15, 16.
+ * The call to mbedtls_gcm_crypt_and_tag or
+ * mbedtls_gcm_auth_decrypt will validate the tag length. */
+ if (PSA_BLOCK_CIPHER_BLOCK_LENGTH(attributes->type) != 16) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ mbedtls_gcm_init(&operation->ctx.gcm);
+ status = mbedtls_to_psa_error(
+ mbedtls_gcm_setkey(&operation->ctx.gcm, cipher_id,
+ key_buffer, (unsigned int) key_bits));
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+ break;
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_GCM */
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305)
+ case PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CHACHA20_POLY1305, 0):
+ operation->alg = PSA_ALG_CHACHA20_POLY1305;
+ /* We only support the default tag length. */
+ if (alg != PSA_ALG_CHACHA20_POLY1305) {
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+
+ mbedtls_chachapoly_init(&operation->ctx.chachapoly);
+ status = mbedtls_to_psa_error(
+ mbedtls_chachapoly_setkey(&operation->ctx.chachapoly,
+ key_buffer));
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+ break;
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305 */
+
+ default:
+ (void) status;
+ (void) key_buffer;
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+
+ operation->key_type = psa_get_key_type(attributes);
+
+ operation->tag_length = PSA_ALG_AEAD_GET_TAG_LENGTH(alg);
+
+ return PSA_SUCCESS;
+}
+
+psa_status_t mbedtls_psa_aead_encrypt(
+ const psa_key_attributes_t *attributes,
+ const uint8_t *key_buffer, size_t key_buffer_size,
+ psa_algorithm_t alg,
+ const uint8_t *nonce, size_t nonce_length,
+ const uint8_t *additional_data, size_t additional_data_length,
+ const uint8_t *plaintext, size_t plaintext_length,
+ uint8_t *ciphertext, size_t ciphertext_size, size_t *ciphertext_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ mbedtls_psa_aead_operation_t operation = MBEDTLS_PSA_AEAD_OPERATION_INIT;
+ uint8_t *tag;
+
+ status = psa_aead_setup(&operation, attributes, key_buffer,
+ key_buffer_size, alg);
+
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ /* For all currently supported modes, the tag is at the end of the
+ * ciphertext. */
+ if (ciphertext_size < (plaintext_length + operation.tag_length)) {
+ status = PSA_ERROR_BUFFER_TOO_SMALL;
+ goto exit;
+ }
+ tag = ciphertext + plaintext_length;
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CCM)
+ if (operation.alg == PSA_ALG_CCM) {
+ status = mbedtls_to_psa_error(
+ mbedtls_ccm_encrypt_and_tag(&operation.ctx.ccm,
+ plaintext_length,
+ nonce, nonce_length,
+ additional_data,
+ additional_data_length,
+ plaintext, ciphertext,
+ tag, operation.tag_length));
+ } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_CCM */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_GCM)
+ if (operation.alg == PSA_ALG_GCM) {
+ status = mbedtls_to_psa_error(
+ mbedtls_gcm_crypt_and_tag(&operation.ctx.gcm,
+ MBEDTLS_GCM_ENCRYPT,
+ plaintext_length,
+ nonce, nonce_length,
+ additional_data, additional_data_length,
+ plaintext, ciphertext,
+ operation.tag_length, tag));
+ } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_GCM */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305)
+ if (operation.alg == PSA_ALG_CHACHA20_POLY1305) {
+ if (operation.tag_length != 16) {
+ status = PSA_ERROR_NOT_SUPPORTED;
+ goto exit;
+ }
+ status = mbedtls_to_psa_error(
+ mbedtls_chachapoly_encrypt_and_tag(&operation.ctx.chachapoly,
+ plaintext_length,
+ nonce,
+ additional_data,
+ additional_data_length,
+ plaintext,
+ ciphertext,
+ tag));
+ } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305 */
+ {
+ (void) tag;
+ (void) nonce;
+ (void) nonce_length;
+ (void) additional_data;
+ (void) additional_data_length;
+ (void) plaintext;
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+
+ if (status == PSA_SUCCESS) {
+ *ciphertext_length = plaintext_length + operation.tag_length;
+ }
+
+exit:
+ mbedtls_psa_aead_abort(&operation);
+
+ return status;
+}
+
+/* Locate the tag in a ciphertext buffer containing the encrypted data
+ * followed by the tag. Return the length of the part preceding the tag in
+ * *plaintext_length. This is the size of the plaintext in modes where
+ * the encrypted data has the same size as the plaintext, such as
+ * CCM and GCM. */
+static psa_status_t psa_aead_unpadded_locate_tag(size_t tag_length,
+ const uint8_t *ciphertext,
+ size_t ciphertext_length,
+ size_t plaintext_size,
+ const uint8_t **p_tag)
+{
+ size_t payload_length;
+ if (tag_length > ciphertext_length) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+ payload_length = ciphertext_length - tag_length;
+ if (payload_length > plaintext_size) {
+ return PSA_ERROR_BUFFER_TOO_SMALL;
+ }
+ *p_tag = ciphertext + payload_length;
+ return PSA_SUCCESS;
+}
+
+psa_status_t mbedtls_psa_aead_decrypt(
+ const psa_key_attributes_t *attributes,
+ const uint8_t *key_buffer, size_t key_buffer_size,
+ psa_algorithm_t alg,
+ const uint8_t *nonce, size_t nonce_length,
+ const uint8_t *additional_data, size_t additional_data_length,
+ const uint8_t *ciphertext, size_t ciphertext_length,
+ uint8_t *plaintext, size_t plaintext_size, size_t *plaintext_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ mbedtls_psa_aead_operation_t operation = MBEDTLS_PSA_AEAD_OPERATION_INIT;
+ const uint8_t *tag = NULL;
+
+ status = psa_aead_setup(&operation, attributes, key_buffer,
+ key_buffer_size, alg);
+
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ status = psa_aead_unpadded_locate_tag(operation.tag_length,
+ ciphertext, ciphertext_length,
+ plaintext_size, &tag);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CCM)
+ if (operation.alg == PSA_ALG_CCM) {
+ status = mbedtls_to_psa_error(
+ mbedtls_ccm_auth_decrypt(&operation.ctx.ccm,
+ ciphertext_length - operation.tag_length,
+ nonce, nonce_length,
+ additional_data,
+ additional_data_length,
+ ciphertext, plaintext,
+ tag, operation.tag_length));
+ } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_CCM */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_GCM)
+ if (operation.alg == PSA_ALG_GCM) {
+ status = mbedtls_to_psa_error(
+ mbedtls_gcm_auth_decrypt(&operation.ctx.gcm,
+ ciphertext_length - operation.tag_length,
+ nonce, nonce_length,
+ additional_data,
+ additional_data_length,
+ tag, operation.tag_length,
+ ciphertext, plaintext));
+ } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_GCM */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305)
+ if (operation.alg == PSA_ALG_CHACHA20_POLY1305) {
+ if (operation.tag_length != 16) {
+ status = PSA_ERROR_NOT_SUPPORTED;
+ goto exit;
+ }
+ status = mbedtls_to_psa_error(
+ mbedtls_chachapoly_auth_decrypt(&operation.ctx.chachapoly,
+ ciphertext_length - operation.tag_length,
+ nonce,
+ additional_data,
+ additional_data_length,
+ tag,
+ ciphertext,
+ plaintext));
+ } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305 */
+ {
+ (void) nonce;
+ (void) nonce_length;
+ (void) additional_data;
+ (void) additional_data_length;
+ (void) plaintext;
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+
+ if (status == PSA_SUCCESS) {
+ *plaintext_length = ciphertext_length - operation.tag_length;
+ }
+
+exit:
+ mbedtls_psa_aead_abort(&operation);
+
+ if (status == PSA_SUCCESS) {
+ *plaintext_length = ciphertext_length - operation.tag_length;
+ }
+ return status;
+}
+
+/* Set the key and algorithm for a multipart authenticated encryption
+ * operation. */
+psa_status_t mbedtls_psa_aead_encrypt_setup(
+ mbedtls_psa_aead_operation_t *operation,
+ const psa_key_attributes_t *attributes,
+ const uint8_t *key_buffer,
+ size_t key_buffer_size,
+ psa_algorithm_t alg)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+ status = psa_aead_setup(operation, attributes, key_buffer,
+ key_buffer_size, alg);
+
+ if (status == PSA_SUCCESS) {
+ operation->is_encrypt = 1;
+ }
+
+ return status;
+}
+
+/* Set the key and algorithm for a multipart authenticated decryption
+ * operation. */
+psa_status_t mbedtls_psa_aead_decrypt_setup(
+ mbedtls_psa_aead_operation_t *operation,
+ const psa_key_attributes_t *attributes,
+ const uint8_t *key_buffer,
+ size_t key_buffer_size,
+ psa_algorithm_t alg)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+ status = psa_aead_setup(operation, attributes, key_buffer,
+ key_buffer_size, alg);
+
+ if (status == PSA_SUCCESS) {
+ operation->is_encrypt = 0;
+ }
+
+ return status;
+}
+
+/* Set a nonce for the multipart AEAD operation*/
+psa_status_t mbedtls_psa_aead_set_nonce(
+ mbedtls_psa_aead_operation_t *operation,
+ const uint8_t *nonce,
+ size_t nonce_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_GCM)
+ if (operation->alg == PSA_ALG_GCM) {
+ status = mbedtls_to_psa_error(
+ mbedtls_gcm_starts(&operation->ctx.gcm,
+ operation->is_encrypt ?
+ MBEDTLS_GCM_ENCRYPT : MBEDTLS_GCM_DECRYPT,
+ nonce,
+ nonce_length));
+ } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_GCM */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CCM)
+ if (operation->alg == PSA_ALG_CCM) {
+ status = mbedtls_to_psa_error(
+ mbedtls_ccm_starts(&operation->ctx.ccm,
+ operation->is_encrypt ?
+ MBEDTLS_CCM_ENCRYPT : MBEDTLS_CCM_DECRYPT,
+ nonce,
+ nonce_length));
+ } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_CCM */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305)
+ if (operation->alg == PSA_ALG_CHACHA20_POLY1305) {
+ /* Note - ChaChaPoly allows an 8 byte nonce, but we would have to
+ * allocate a buffer in the operation, copy the nonce to it and pad
+ * it, so for now check the nonce is 12 bytes, as
+ * mbedtls_chachapoly_starts() assumes it can read 12 bytes from the
+ * passed in buffer. */
+ if (nonce_length != 12) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ status = mbedtls_to_psa_error(
+ mbedtls_chachapoly_starts(&operation->ctx.chachapoly,
+ nonce,
+ operation->is_encrypt ?
+ MBEDTLS_CHACHAPOLY_ENCRYPT :
+ MBEDTLS_CHACHAPOLY_DECRYPT));
+ } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305 */
+ {
+ (void) operation;
+ (void) nonce;
+ (void) nonce_length;
+
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+
+ return status;
+}
+
+/* Declare the lengths of the message and additional data for AEAD. */
+psa_status_t mbedtls_psa_aead_set_lengths(
+ mbedtls_psa_aead_operation_t *operation,
+ size_t ad_length,
+ size_t plaintext_length)
+{
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CCM)
+ if (operation->alg == PSA_ALG_CCM) {
+ return mbedtls_to_psa_error(
+ mbedtls_ccm_set_lengths(&operation->ctx.ccm,
+ ad_length,
+ plaintext_length,
+ operation->tag_length));
+
+ }
+#else /* MBEDTLS_PSA_BUILTIN_ALG_CCM */
+ (void) operation;
+ (void) ad_length;
+ (void) plaintext_length;
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_CCM */
+
+ return PSA_SUCCESS;
+}
+
+/* Pass additional data to an active multipart AEAD operation. */
+psa_status_t mbedtls_psa_aead_update_ad(
+ mbedtls_psa_aead_operation_t *operation,
+ const uint8_t *input,
+ size_t input_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_GCM)
+ if (operation->alg == PSA_ALG_GCM) {
+ status = mbedtls_to_psa_error(
+ mbedtls_gcm_update_ad(&operation->ctx.gcm, input, input_length));
+ } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_GCM */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CCM)
+ if (operation->alg == PSA_ALG_CCM) {
+ status = mbedtls_to_psa_error(
+ mbedtls_ccm_update_ad(&operation->ctx.ccm, input, input_length));
+ } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_CCM */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305)
+ if (operation->alg == PSA_ALG_CHACHA20_POLY1305) {
+ status = mbedtls_to_psa_error(
+ mbedtls_chachapoly_update_aad(&operation->ctx.chachapoly,
+ input,
+ input_length));
+ } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305 */
+ {
+ (void) operation;
+ (void) input;
+ (void) input_length;
+
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+
+ return status;
+}
+
+/* Encrypt or decrypt a message fragment in an active multipart AEAD
+ * operation.*/
+psa_status_t mbedtls_psa_aead_update(
+ mbedtls_psa_aead_operation_t *operation,
+ const uint8_t *input,
+ size_t input_length,
+ uint8_t *output,
+ size_t output_size,
+ size_t *output_length)
+{
+ size_t update_output_length;
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+ update_output_length = input_length;
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_GCM)
+ if (operation->alg == PSA_ALG_GCM) {
+ status = mbedtls_to_psa_error(
+ mbedtls_gcm_update(&operation->ctx.gcm,
+ input, input_length,
+ output, output_size,
+ &update_output_length));
+ } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_GCM */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CCM)
+ if (operation->alg == PSA_ALG_CCM) {
+ if (output_size < input_length) {
+ return PSA_ERROR_BUFFER_TOO_SMALL;
+ }
+
+ status = mbedtls_to_psa_error(
+ mbedtls_ccm_update(&operation->ctx.ccm,
+ input, input_length,
+ output, output_size,
+ &update_output_length));
+ } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_CCM */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305)
+ if (operation->alg == PSA_ALG_CHACHA20_POLY1305) {
+ if (output_size < input_length) {
+ return PSA_ERROR_BUFFER_TOO_SMALL;
+ }
+
+ status = mbedtls_to_psa_error(
+ mbedtls_chachapoly_update(&operation->ctx.chachapoly,
+ input_length,
+ input,
+ output));
+ } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305 */
+ {
+ (void) operation;
+ (void) input;
+ (void) output;
+ (void) output_size;
+
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+
+ if (status == PSA_SUCCESS) {
+ *output_length = update_output_length;
+ }
+
+ return status;
+}
+
+/* Finish encrypting a message in a multipart AEAD operation. */
+psa_status_t mbedtls_psa_aead_finish(
+ mbedtls_psa_aead_operation_t *operation,
+ uint8_t *ciphertext,
+ size_t ciphertext_size,
+ size_t *ciphertext_length,
+ uint8_t *tag,
+ size_t tag_size,
+ size_t *tag_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ size_t finish_output_size = 0;
+
+ if (tag_size < operation->tag_length) {
+ return PSA_ERROR_BUFFER_TOO_SMALL;
+ }
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_GCM)
+ if (operation->alg == PSA_ALG_GCM) {
+ status = mbedtls_to_psa_error(
+ mbedtls_gcm_finish(&operation->ctx.gcm,
+ ciphertext, ciphertext_size, ciphertext_length,
+ tag, operation->tag_length));
+ } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_GCM */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CCM)
+ if (operation->alg == PSA_ALG_CCM) {
+ /* tag must be big enough to store a tag of size passed into set
+ * lengths. */
+ if (tag_size < operation->tag_length) {
+ return PSA_ERROR_BUFFER_TOO_SMALL;
+ }
+
+ status = mbedtls_to_psa_error(
+ mbedtls_ccm_finish(&operation->ctx.ccm,
+ tag, operation->tag_length));
+ } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_CCM */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305)
+ if (operation->alg == PSA_ALG_CHACHA20_POLY1305) {
+ /* Belt and braces. Although the above tag_size check should have
+ * already done this, if we later start supporting smaller tag sizes
+ * for chachapoly, then passing a tag buffer smaller than 16 into here
+ * could cause a buffer overflow, so better safe than sorry. */
+ if (tag_size < 16) {
+ return PSA_ERROR_BUFFER_TOO_SMALL;
+ }
+
+ status = mbedtls_to_psa_error(
+ mbedtls_chachapoly_finish(&operation->ctx.chachapoly,
+ tag));
+ } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305 */
+ {
+ (void) ciphertext;
+ (void) ciphertext_size;
+ (void) ciphertext_length;
+ (void) tag;
+ (void) tag_size;
+ (void) tag_length;
+
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+
+ if (status == PSA_SUCCESS) {
+ /* This will be zero for all supported algorithms currently, but left
+ * here for future support. */
+ *ciphertext_length = finish_output_size;
+ *tag_length = operation->tag_length;
+ }
+
+ return status;
+}
+
+/* Abort an AEAD operation */
+psa_status_t mbedtls_psa_aead_abort(
+ mbedtls_psa_aead_operation_t *operation)
+{
+ switch (operation->alg) {
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CCM)
+ case PSA_ALG_CCM:
+ mbedtls_ccm_free(&operation->ctx.ccm);
+ break;
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_CCM */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_GCM)
+ case PSA_ALG_GCM:
+ mbedtls_gcm_free(&operation->ctx.gcm);
+ break;
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_GCM */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305)
+ case PSA_ALG_CHACHA20_POLY1305:
+ mbedtls_chachapoly_free(&operation->ctx.chachapoly);
+ break;
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305 */
+ }
+
+ operation->is_encrypt = 0;
+
+ return PSA_SUCCESS;
+}
+
+#endif /* MBEDTLS_PSA_CRYPTO_C */
diff --git a/thirdparty/mbedtls/library/psa_crypto_cipher.c b/thirdparty/mbedtls/library/psa_crypto_cipher.c
new file mode 100644
index 0000000000..3216c94898
--- /dev/null
+++ b/thirdparty/mbedtls/library/psa_crypto_cipher.c
@@ -0,0 +1,724 @@
+/*
+ * PSA cipher driver entry points
+ */
+/*
+ * Copyright The Mbed TLS Contributors
+ * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#include "common.h"
+
+#if defined(MBEDTLS_PSA_CRYPTO_C)
+
+#include "psa_crypto_cipher.h"
+#include "psa_crypto_core.h"
+#include "psa_crypto_random_impl.h"
+
+#include "mbedtls/cipher.h"
+#include "mbedtls/error.h"
+
+#include <string.h>
+
+/* mbedtls_cipher_values_from_psa() below only checks if the proper build symbols
+ * are enabled, but it does not provide any compatibility check between them
+ * (i.e. if the specified key works with the specified algorithm). This helper
+ * function is meant to provide this support.
+ * mbedtls_cipher_info_from_psa() might be used for the same purpose, but it
+ * requires CIPHER_C to be enabled.
+ */
+static psa_status_t mbedtls_cipher_validate_values(
+ psa_algorithm_t alg,
+ psa_key_type_t key_type)
+{
+ /* Reduce code size - hinting to the compiler about what it can assume allows the compiler to
+ eliminate bits of the logic below. */
+#if !defined(PSA_WANT_KEY_TYPE_AES)
+ MBEDTLS_ASSUME(key_type != PSA_KEY_TYPE_AES);
+#endif
+#if !defined(PSA_WANT_KEY_TYPE_ARIA)
+ MBEDTLS_ASSUME(key_type != PSA_KEY_TYPE_ARIA);
+#endif
+#if !defined(PSA_WANT_KEY_TYPE_CAMELLIA)
+ MBEDTLS_ASSUME(key_type != PSA_KEY_TYPE_CAMELLIA);
+#endif
+#if !defined(PSA_WANT_KEY_TYPE_CHACHA20)
+ MBEDTLS_ASSUME(key_type != PSA_KEY_TYPE_CHACHA20);
+#endif
+#if !defined(PSA_WANT_KEY_TYPE_DES)
+ MBEDTLS_ASSUME(key_type != PSA_KEY_TYPE_DES);
+#endif
+#if !defined(PSA_WANT_ALG_CCM)
+ MBEDTLS_ASSUME(alg != PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, 0));
+#endif
+#if !defined(PSA_WANT_ALG_GCM)
+ MBEDTLS_ASSUME(alg != PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_GCM, 0));
+#endif
+#if !defined(PSA_WANT_ALG_STREAM_CIPHER)
+ MBEDTLS_ASSUME(alg != PSA_ALG_STREAM_CIPHER);
+#endif
+#if !defined(PSA_WANT_ALG_CHACHA20_POLY1305)
+ MBEDTLS_ASSUME(alg != PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CHACHA20_POLY1305, 0));
+#endif
+#if !defined(PSA_WANT_ALG_CCM_STAR_NO_TAG)
+ MBEDTLS_ASSUME(alg != PSA_ALG_CCM_STAR_NO_TAG);
+#endif
+#if !defined(PSA_WANT_ALG_CTR)
+ MBEDTLS_ASSUME(alg != PSA_ALG_CTR);
+#endif
+#if !defined(PSA_WANT_ALG_CFB)
+ MBEDTLS_ASSUME(alg != PSA_ALG_CFB);
+#endif
+#if !defined(PSA_WANT_ALG_OFB)
+ MBEDTLS_ASSUME(alg != PSA_ALG_OFB);
+#endif
+#if !defined(PSA_WANT_ALG_XTS)
+ MBEDTLS_ASSUME(alg != PSA_ALG_XTS);
+#endif
+#if !defined(PSA_WANT_ALG_ECB_NO_PADDING)
+ MBEDTLS_ASSUME(alg != PSA_ALG_ECB_NO_PADDING);
+#endif
+#if !defined(PSA_WANT_ALG_CBC_NO_PADDING)
+ MBEDTLS_ASSUME(alg != PSA_ALG_CBC_NO_PADDING);
+#endif
+#if !defined(PSA_WANT_ALG_CBC_PKCS7)
+ MBEDTLS_ASSUME(alg != PSA_ALG_CBC_PKCS7);
+#endif
+#if !defined(PSA_WANT_ALG_CMAC)
+ MBEDTLS_ASSUME(alg != PSA_ALG_CMAC);
+#endif
+
+ if (alg == PSA_ALG_STREAM_CIPHER ||
+ alg == PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CHACHA20_POLY1305, 0)) {
+ if (key_type == PSA_KEY_TYPE_CHACHA20) {
+ return PSA_SUCCESS;
+ }
+ }
+
+ if (alg == PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, 0) ||
+ alg == PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_GCM, 0) ||
+ alg == PSA_ALG_CCM_STAR_NO_TAG) {
+ if (key_type == PSA_KEY_TYPE_AES ||
+ key_type == PSA_KEY_TYPE_ARIA ||
+ key_type == PSA_KEY_TYPE_CAMELLIA) {
+ return PSA_SUCCESS;
+ }
+ }
+
+ if (alg == PSA_ALG_CTR ||
+ alg == PSA_ALG_CFB ||
+ alg == PSA_ALG_OFB ||
+ alg == PSA_ALG_XTS ||
+ alg == PSA_ALG_ECB_NO_PADDING ||
+ alg == PSA_ALG_CBC_NO_PADDING ||
+ alg == PSA_ALG_CBC_PKCS7 ||
+ alg == PSA_ALG_CMAC) {
+ if (key_type == PSA_KEY_TYPE_AES ||
+ key_type == PSA_KEY_TYPE_ARIA ||
+ key_type == PSA_KEY_TYPE_DES ||
+ key_type == PSA_KEY_TYPE_CAMELLIA) {
+ return PSA_SUCCESS;
+ }
+ }
+
+ return PSA_ERROR_NOT_SUPPORTED;
+}
+
+psa_status_t mbedtls_cipher_values_from_psa(
+ psa_algorithm_t alg,
+ psa_key_type_t key_type,
+ size_t *key_bits,
+ mbedtls_cipher_mode_t *mode,
+ mbedtls_cipher_id_t *cipher_id)
+{
+ mbedtls_cipher_id_t cipher_id_tmp;
+ /* Only DES modifies key_bits */
+#if !defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DES)
+ (void) key_bits;
+#endif
+
+ if (PSA_ALG_IS_AEAD(alg)) {
+ alg = PSA_ALG_AEAD_WITH_SHORTENED_TAG(alg, 0);
+ }
+
+ if (PSA_ALG_IS_CIPHER(alg) || PSA_ALG_IS_AEAD(alg)) {
+ switch (alg) {
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_STREAM_CIPHER)
+ case PSA_ALG_STREAM_CIPHER:
+ *mode = MBEDTLS_MODE_STREAM;
+ break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CTR)
+ case PSA_ALG_CTR:
+ *mode = MBEDTLS_MODE_CTR;
+ break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CFB)
+ case PSA_ALG_CFB:
+ *mode = MBEDTLS_MODE_CFB;
+ break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_OFB)
+ case PSA_ALG_OFB:
+ *mode = MBEDTLS_MODE_OFB;
+ break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_ECB_NO_PADDING)
+ case PSA_ALG_ECB_NO_PADDING:
+ *mode = MBEDTLS_MODE_ECB;
+ break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CBC_NO_PADDING)
+ case PSA_ALG_CBC_NO_PADDING:
+ *mode = MBEDTLS_MODE_CBC;
+ break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CBC_PKCS7)
+ case PSA_ALG_CBC_PKCS7:
+ *mode = MBEDTLS_MODE_CBC;
+ break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CCM_STAR_NO_TAG)
+ case PSA_ALG_CCM_STAR_NO_TAG:
+ *mode = MBEDTLS_MODE_CCM_STAR_NO_TAG;
+ break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CCM)
+ case PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, 0):
+ *mode = MBEDTLS_MODE_CCM;
+ break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_GCM)
+ case PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_GCM, 0):
+ *mode = MBEDTLS_MODE_GCM;
+ break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CHACHA20_POLY1305)
+ case PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CHACHA20_POLY1305, 0):
+ *mode = MBEDTLS_MODE_CHACHAPOLY;
+ break;
+#endif
+ default:
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+ } else if (alg == PSA_ALG_CMAC) {
+ *mode = MBEDTLS_MODE_ECB;
+ } else {
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+
+ switch (key_type) {
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_AES)
+ case PSA_KEY_TYPE_AES:
+ cipher_id_tmp = MBEDTLS_CIPHER_ID_AES;
+ break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ARIA)
+ case PSA_KEY_TYPE_ARIA:
+ cipher_id_tmp = MBEDTLS_CIPHER_ID_ARIA;
+ break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DES)
+ case PSA_KEY_TYPE_DES:
+ /* key_bits is 64 for Single-DES, 128 for two-key Triple-DES,
+ * and 192 for three-key Triple-DES. */
+ if (*key_bits == 64) {
+ cipher_id_tmp = MBEDTLS_CIPHER_ID_DES;
+ } else {
+ cipher_id_tmp = MBEDTLS_CIPHER_ID_3DES;
+ }
+ /* mbedtls doesn't recognize two-key Triple-DES as an algorithm,
+ * but two-key Triple-DES is functionally three-key Triple-DES
+ * with K1=K3, so that's how we present it to mbedtls. */
+ if (*key_bits == 128) {
+ *key_bits = 192;
+ }
+ break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_CAMELLIA)
+ case PSA_KEY_TYPE_CAMELLIA:
+ cipher_id_tmp = MBEDTLS_CIPHER_ID_CAMELLIA;
+ break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_CHACHA20)
+ case PSA_KEY_TYPE_CHACHA20:
+ cipher_id_tmp = MBEDTLS_CIPHER_ID_CHACHA20;
+ break;
+#endif
+ default:
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+ if (cipher_id != NULL) {
+ *cipher_id = cipher_id_tmp;
+ }
+
+ return mbedtls_cipher_validate_values(alg, key_type);
+}
+
+#if defined(MBEDTLS_CIPHER_C)
+const mbedtls_cipher_info_t *mbedtls_cipher_info_from_psa(
+ psa_algorithm_t alg,
+ psa_key_type_t key_type,
+ size_t key_bits,
+ mbedtls_cipher_id_t *cipher_id)
+{
+ mbedtls_cipher_mode_t mode;
+ psa_status_t status;
+ mbedtls_cipher_id_t cipher_id_tmp = MBEDTLS_CIPHER_ID_NONE;
+
+ status = mbedtls_cipher_values_from_psa(alg, key_type, &key_bits, &mode, &cipher_id_tmp);
+ if (status != PSA_SUCCESS) {
+ return NULL;
+ }
+ if (cipher_id != NULL) {
+ *cipher_id = cipher_id_tmp;
+ }
+
+ return mbedtls_cipher_info_from_values(cipher_id_tmp, (int) key_bits, mode);
+}
+#endif /* MBEDTLS_CIPHER_C */
+
+#if defined(MBEDTLS_PSA_BUILTIN_CIPHER)
+
+static psa_status_t psa_cipher_setup(
+ mbedtls_psa_cipher_operation_t *operation,
+ const psa_key_attributes_t *attributes,
+ const uint8_t *key_buffer, size_t key_buffer_size,
+ psa_algorithm_t alg,
+ mbedtls_operation_t cipher_operation)
+{
+ int ret = 0;
+ size_t key_bits;
+ const mbedtls_cipher_info_t *cipher_info = NULL;
+ psa_key_type_t key_type = attributes->type;
+
+ (void) key_buffer_size;
+
+ mbedtls_cipher_init(&operation->ctx.cipher);
+
+ operation->alg = alg;
+ key_bits = attributes->bits;
+ cipher_info = mbedtls_cipher_info_from_psa(alg, key_type,
+ key_bits, NULL);
+ if (cipher_info == NULL) {
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+
+ ret = mbedtls_cipher_setup(&operation->ctx.cipher, cipher_info);
+ if (ret != 0) {
+ goto exit;
+ }
+
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DES)
+ if (key_type == PSA_KEY_TYPE_DES && key_bits == 128) {
+ /* Two-key Triple-DES is 3-key Triple-DES with K1=K3 */
+ uint8_t keys[24];
+ memcpy(keys, key_buffer, 16);
+ memcpy(keys + 16, key_buffer, 8);
+ ret = mbedtls_cipher_setkey(&operation->ctx.cipher,
+ keys,
+ 192, cipher_operation);
+ } else
+#endif
+ {
+ ret = mbedtls_cipher_setkey(&operation->ctx.cipher, key_buffer,
+ (int) key_bits, cipher_operation);
+ }
+ if (ret != 0) {
+ goto exit;
+ }
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CBC_NO_PADDING) || \
+ defined(MBEDTLS_PSA_BUILTIN_ALG_CBC_PKCS7)
+ switch (alg) {
+ case PSA_ALG_CBC_NO_PADDING:
+ ret = mbedtls_cipher_set_padding_mode(&operation->ctx.cipher,
+ MBEDTLS_PADDING_NONE);
+ break;
+ case PSA_ALG_CBC_PKCS7:
+ ret = mbedtls_cipher_set_padding_mode(&operation->ctx.cipher,
+ MBEDTLS_PADDING_PKCS7);
+ break;
+ default:
+ /* The algorithm doesn't involve padding. */
+ ret = 0;
+ break;
+ }
+ if (ret != 0) {
+ goto exit;
+ }
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_CBC_NO_PADDING ||
+ MBEDTLS_PSA_BUILTIN_ALG_CBC_PKCS7 */
+
+ operation->block_length = (PSA_ALG_IS_STREAM_CIPHER(alg) ? 1 :
+ PSA_BLOCK_CIPHER_BLOCK_LENGTH(key_type));
+ operation->iv_length = PSA_CIPHER_IV_LENGTH(key_type, alg);
+
+exit:
+ return mbedtls_to_psa_error(ret);
+}
+
+psa_status_t mbedtls_psa_cipher_encrypt_setup(
+ mbedtls_psa_cipher_operation_t *operation,
+ const psa_key_attributes_t *attributes,
+ const uint8_t *key_buffer, size_t key_buffer_size,
+ psa_algorithm_t alg)
+{
+ return psa_cipher_setup(operation, attributes,
+ key_buffer, key_buffer_size,
+ alg, MBEDTLS_ENCRYPT);
+}
+
+psa_status_t mbedtls_psa_cipher_decrypt_setup(
+ mbedtls_psa_cipher_operation_t *operation,
+ const psa_key_attributes_t *attributes,
+ const uint8_t *key_buffer, size_t key_buffer_size,
+ psa_algorithm_t alg)
+{
+ return psa_cipher_setup(operation, attributes,
+ key_buffer, key_buffer_size,
+ alg, MBEDTLS_DECRYPT);
+}
+
+psa_status_t mbedtls_psa_cipher_set_iv(
+ mbedtls_psa_cipher_operation_t *operation,
+ const uint8_t *iv, size_t iv_length)
+{
+ if (iv_length != operation->iv_length) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ return mbedtls_to_psa_error(
+ mbedtls_cipher_set_iv(&operation->ctx.cipher,
+ iv, iv_length));
+}
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_ECB_NO_PADDING)
+/** Process input for which the algorithm is set to ECB mode.
+ *
+ * This requires manual processing, since the PSA API is defined as being
+ * able to process arbitrary-length calls to psa_cipher_update() with ECB mode,
+ * but the underlying mbedtls_cipher_update only takes full blocks.
+ *
+ * \param ctx The mbedtls cipher context to use. It must have been
+ * set up for ECB.
+ * \param[in] input The input plaintext or ciphertext to process.
+ * \param input_length The number of bytes to process from \p input.
+ * This does not need to be aligned to a block boundary.
+ * If there is a partial block at the end of the input,
+ * it is stored in \p ctx for future processing.
+ * \param output The buffer where the output is written. It must be
+ * at least `BS * floor((p + input_length) / BS)` bytes
+ * long, where `p` is the number of bytes in the
+ * unprocessed partial block in \p ctx (with
+ * `0 <= p <= BS - 1`) and `BS` is the block size.
+ * \param output_length On success, the number of bytes written to \p output.
+ * \c 0 on error.
+ *
+ * \return #PSA_SUCCESS or an error from a hardware accelerator
+ */
+static psa_status_t psa_cipher_update_ecb(
+ mbedtls_cipher_context_t *ctx,
+ const uint8_t *input,
+ size_t input_length,
+ uint8_t *output,
+ size_t *output_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ size_t block_size = mbedtls_cipher_info_get_block_size(ctx->cipher_info);
+ size_t internal_output_length = 0;
+ *output_length = 0;
+
+ if (input_length == 0) {
+ status = PSA_SUCCESS;
+ goto exit;
+ }
+
+ if (ctx->unprocessed_len > 0) {
+ /* Fill up to block size, and run the block if there's a full one. */
+ size_t bytes_to_copy = block_size - ctx->unprocessed_len;
+
+ if (input_length < bytes_to_copy) {
+ bytes_to_copy = input_length;
+ }
+
+ memcpy(&(ctx->unprocessed_data[ctx->unprocessed_len]),
+ input, bytes_to_copy);
+ input_length -= bytes_to_copy;
+ input += bytes_to_copy;
+ ctx->unprocessed_len += bytes_to_copy;
+
+ if (ctx->unprocessed_len == block_size) {
+ status = mbedtls_to_psa_error(
+ mbedtls_cipher_update(ctx,
+ ctx->unprocessed_data,
+ block_size,
+ output, &internal_output_length));
+
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ output += internal_output_length;
+ *output_length += internal_output_length;
+ ctx->unprocessed_len = 0;
+ }
+ }
+
+ while (input_length >= block_size) {
+ /* Run all full blocks we have, one by one */
+ status = mbedtls_to_psa_error(
+ mbedtls_cipher_update(ctx, input,
+ block_size,
+ output, &internal_output_length));
+
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ input_length -= block_size;
+ input += block_size;
+
+ output += internal_output_length;
+ *output_length += internal_output_length;
+ }
+
+ if (input_length > 0) {
+ /* Save unprocessed bytes for later processing */
+ memcpy(&(ctx->unprocessed_data[ctx->unprocessed_len]),
+ input, input_length);
+ ctx->unprocessed_len += input_length;
+ }
+
+ status = PSA_SUCCESS;
+
+exit:
+ return status;
+}
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_ECB_NO_PADDING */
+
+psa_status_t mbedtls_psa_cipher_update(
+ mbedtls_psa_cipher_operation_t *operation,
+ const uint8_t *input, size_t input_length,
+ uint8_t *output, size_t output_size, size_t *output_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ size_t expected_output_size;
+
+ if (!PSA_ALG_IS_STREAM_CIPHER(operation->alg)) {
+ /* Take the unprocessed partial block left over from previous
+ * update calls, if any, plus the input to this call. Remove
+ * the last partial block, if any. You get the data that will be
+ * output in this call. */
+ expected_output_size =
+ (operation->ctx.cipher.unprocessed_len + input_length)
+ / operation->block_length * operation->block_length;
+ } else {
+ expected_output_size = input_length;
+ }
+
+ if (output_size < expected_output_size) {
+ return PSA_ERROR_BUFFER_TOO_SMALL;
+ }
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_ECB_NO_PADDING)
+ if (operation->alg == PSA_ALG_ECB_NO_PADDING) {
+ /* mbedtls_cipher_update has an API inconsistency: it will only
+ * process a single block at a time in ECB mode. Abstract away that
+ * inconsistency here to match the PSA API behaviour. */
+ status = psa_cipher_update_ecb(&operation->ctx.cipher,
+ input,
+ input_length,
+ output,
+ output_length);
+ } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_ECB_NO_PADDING */
+ if (input_length == 0) {
+ /* There is no input, nothing to be done */
+ *output_length = 0;
+ status = PSA_SUCCESS;
+ } else {
+ status = mbedtls_to_psa_error(
+ mbedtls_cipher_update(&operation->ctx.cipher, input,
+ input_length, output, output_length));
+
+ if (*output_length > output_size) {
+ return PSA_ERROR_CORRUPTION_DETECTED;
+ }
+ }
+
+ return status;
+}
+
+psa_status_t mbedtls_psa_cipher_finish(
+ mbedtls_psa_cipher_operation_t *operation,
+ uint8_t *output, size_t output_size, size_t *output_length)
+{
+ psa_status_t status = PSA_ERROR_GENERIC_ERROR;
+ uint8_t temp_output_buffer[MBEDTLS_MAX_BLOCK_LENGTH];
+
+ if (operation->ctx.cipher.unprocessed_len != 0) {
+ if (operation->alg == PSA_ALG_ECB_NO_PADDING ||
+ operation->alg == PSA_ALG_CBC_NO_PADDING) {
+ status = PSA_ERROR_INVALID_ARGUMENT;
+ goto exit;
+ }
+ }
+
+ status = mbedtls_to_psa_error(
+ mbedtls_cipher_finish(&operation->ctx.cipher,
+ temp_output_buffer,
+ output_length));
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ if (*output_length == 0) {
+ ; /* Nothing to copy. Note that output may be NULL in this case. */
+ } else if (output_size >= *output_length) {
+ memcpy(output, temp_output_buffer, *output_length);
+ } else {
+ status = PSA_ERROR_BUFFER_TOO_SMALL;
+ }
+
+exit:
+ mbedtls_platform_zeroize(temp_output_buffer,
+ sizeof(temp_output_buffer));
+
+ return status;
+}
+
+psa_status_t mbedtls_psa_cipher_abort(
+ mbedtls_psa_cipher_operation_t *operation)
+{
+ /* Sanity check (shouldn't happen: operation->alg should
+ * always have been initialized to a valid value). */
+ if (!PSA_ALG_IS_CIPHER(operation->alg)) {
+ return PSA_ERROR_BAD_STATE;
+ }
+
+ mbedtls_cipher_free(&operation->ctx.cipher);
+
+ return PSA_SUCCESS;
+}
+
+psa_status_t mbedtls_psa_cipher_encrypt(
+ const psa_key_attributes_t *attributes,
+ const uint8_t *key_buffer,
+ size_t key_buffer_size,
+ psa_algorithm_t alg,
+ const uint8_t *iv,
+ size_t iv_length,
+ const uint8_t *input,
+ size_t input_length,
+ uint8_t *output,
+ size_t output_size,
+ size_t *output_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ mbedtls_psa_cipher_operation_t operation = MBEDTLS_PSA_CIPHER_OPERATION_INIT;
+ size_t update_output_length, finish_output_length;
+
+ status = mbedtls_psa_cipher_encrypt_setup(&operation, attributes,
+ key_buffer, key_buffer_size,
+ alg);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ if (iv_length > 0) {
+ status = mbedtls_psa_cipher_set_iv(&operation, iv, iv_length);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+ }
+
+ status = mbedtls_psa_cipher_update(&operation, input, input_length,
+ output, output_size,
+ &update_output_length);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ status = mbedtls_psa_cipher_finish(
+ &operation,
+ mbedtls_buffer_offset(output, update_output_length),
+ output_size - update_output_length, &finish_output_length);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ *output_length = update_output_length + finish_output_length;
+
+exit:
+ if (status == PSA_SUCCESS) {
+ status = mbedtls_psa_cipher_abort(&operation);
+ } else {
+ mbedtls_psa_cipher_abort(&operation);
+ }
+
+ return status;
+}
+
+psa_status_t mbedtls_psa_cipher_decrypt(
+ const psa_key_attributes_t *attributes,
+ const uint8_t *key_buffer,
+ size_t key_buffer_size,
+ psa_algorithm_t alg,
+ const uint8_t *input,
+ size_t input_length,
+ uint8_t *output,
+ size_t output_size,
+ size_t *output_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ mbedtls_psa_cipher_operation_t operation = MBEDTLS_PSA_CIPHER_OPERATION_INIT;
+ size_t olength, accumulated_length;
+
+ status = mbedtls_psa_cipher_decrypt_setup(&operation, attributes,
+ key_buffer, key_buffer_size,
+ alg);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ if (operation.iv_length > 0) {
+ status = mbedtls_psa_cipher_set_iv(&operation,
+ input, operation.iv_length);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+ }
+
+ status = mbedtls_psa_cipher_update(
+ &operation,
+ mbedtls_buffer_offset_const(input, operation.iv_length),
+ input_length - operation.iv_length,
+ output, output_size, &olength);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ accumulated_length = olength;
+
+ status = mbedtls_psa_cipher_finish(
+ &operation,
+ mbedtls_buffer_offset(output, accumulated_length),
+ output_size - accumulated_length, &olength);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ *output_length = accumulated_length + olength;
+
+exit:
+ if (status == PSA_SUCCESS) {
+ status = mbedtls_psa_cipher_abort(&operation);
+ } else {
+ mbedtls_psa_cipher_abort(&operation);
+ }
+
+ return status;
+}
+#endif /* MBEDTLS_PSA_BUILTIN_CIPHER */
+
+#endif /* MBEDTLS_PSA_CRYPTO_C */
diff --git a/thirdparty/mbedtls/library/psa_crypto_client.c b/thirdparty/mbedtls/library/psa_crypto_client.c
new file mode 100644
index 0000000000..72f671d63d
--- /dev/null
+++ b/thirdparty/mbedtls/library/psa_crypto_client.c
@@ -0,0 +1,22 @@
+/*
+ * PSA crypto client code
+ */
+/*
+ * Copyright The Mbed TLS Contributors
+ * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#include "common.h"
+#include "psa/crypto.h"
+
+#if defined(MBEDTLS_PSA_CRYPTO_CLIENT)
+
+#include <string.h>
+#include "mbedtls/platform.h"
+
+void psa_reset_key_attributes(psa_key_attributes_t *attributes)
+{
+ memset(attributes, 0, sizeof(*attributes));
+}
+
+#endif /* MBEDTLS_PSA_CRYPTO_CLIENT */
diff --git a/thirdparty/mbedtls/library/psa_crypto_driver_wrappers_no_static.c b/thirdparty/mbedtls/library/psa_crypto_driver_wrappers_no_static.c
new file mode 100644
index 0000000000..de8a5269b3
--- /dev/null
+++ b/thirdparty/mbedtls/library/psa_crypto_driver_wrappers_no_static.c
@@ -0,0 +1,256 @@
+/*
+ * Functions to delegate cryptographic operations to an available
+ * and appropriate accelerator.
+ * Warning: This file is now auto-generated.
+ */
+/* Copyright The Mbed TLS Contributors
+ * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+
+/* BEGIN-common headers */
+#include "common.h"
+#include "psa_crypto_aead.h"
+#include "psa_crypto_cipher.h"
+#include "psa_crypto_core.h"
+#include "psa_crypto_driver_wrappers_no_static.h"
+#include "psa_crypto_hash.h"
+#include "psa_crypto_mac.h"
+#include "psa_crypto_pake.h"
+#include "psa_crypto_rsa.h"
+
+#include "mbedtls/platform.h"
+/* END-common headers */
+
+#if defined(MBEDTLS_PSA_CRYPTO_C)
+
+/* BEGIN-driver headers */
+/* Headers for mbedtls_test opaque driver */
+#if defined(PSA_CRYPTO_DRIVER_TEST)
+#include "test/drivers/test_driver.h"
+
+#endif
+/* Headers for mbedtls_test transparent driver */
+#if defined(PSA_CRYPTO_DRIVER_TEST)
+#include "test/drivers/test_driver.h"
+
+#endif
+/* Headers for p256 transparent driver */
+#if defined(MBEDTLS_PSA_P256M_DRIVER_ENABLED)
+#include "../3rdparty/p256-m/p256-m_driver_entrypoints.h"
+
+#endif
+
+/* END-driver headers */
+
+/* Auto-generated values depending on which drivers are registered.
+ * ID 0 is reserved for unallocated operations.
+ * ID 1 is reserved for the Mbed TLS software driver. */
+/* BEGIN-driver id definition */
+#define PSA_CRYPTO_MBED_TLS_DRIVER_ID (1)
+#define MBEDTLS_TEST_OPAQUE_DRIVER_ID (2)
+#define MBEDTLS_TEST_TRANSPARENT_DRIVER_ID (3)
+#define P256_TRANSPARENT_DRIVER_ID (4)
+
+/* END-driver id */
+
+/* BEGIN-Common Macro definitions */
+
+/* END-Common Macro definitions */
+
+/* Support the 'old' SE interface when asked to */
+#if defined(MBEDTLS_PSA_CRYPTO_SE_C)
+/* PSA_CRYPTO_DRIVER_PRESENT is defined when either a new-style or old-style
+ * SE driver is present, to avoid unused argument errors at compile time. */
+#ifndef PSA_CRYPTO_DRIVER_PRESENT
+#define PSA_CRYPTO_DRIVER_PRESENT
+#endif
+#include "psa_crypto_se.h"
+#endif
+
+/** Get the key buffer size required to store the key material of a key
+ * associated with an opaque driver.
+ *
+ * \param[in] attributes The key attributes.
+ * \param[out] key_buffer_size Minimum buffer size to contain the key material
+ *
+ * \retval #PSA_SUCCESS
+ * The minimum size for a buffer to contain the key material has been
+ * returned successfully.
+ * \retval #PSA_ERROR_NOT_SUPPORTED
+ * The type and/or the size in bits of the key or the combination of
+ * the two is not supported.
+ * \retval #PSA_ERROR_INVALID_ARGUMENT
+ * The key is declared with a lifetime not known to us.
+ */
+psa_status_t psa_driver_wrapper_get_key_buffer_size(
+ const psa_key_attributes_t *attributes,
+ size_t *key_buffer_size )
+{
+ psa_key_location_t location = PSA_KEY_LIFETIME_GET_LOCATION( psa_get_key_lifetime(attributes) );
+ psa_key_type_t key_type = psa_get_key_type(attributes);
+ size_t key_bits = psa_get_key_bits(attributes);
+
+ *key_buffer_size = 0;
+ switch( location )
+ {
+#if defined(PSA_CRYPTO_DRIVER_TEST)
+ case PSA_CRYPTO_TEST_DRIVER_LOCATION:
+#if defined(MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS)
+ /* Emulate property 'builtin_key_size' */
+ if( psa_key_id_is_builtin(
+ MBEDTLS_SVC_KEY_ID_GET_KEY_ID(
+ psa_get_key_id( attributes ) ) ) )
+ {
+ *key_buffer_size = sizeof( psa_drv_slot_number_t );
+ return( PSA_SUCCESS );
+ }
+#endif /* MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS */
+ *key_buffer_size = mbedtls_test_opaque_size_function( key_type,
+ key_bits );
+ return( ( *key_buffer_size != 0 ) ?
+ PSA_SUCCESS : PSA_ERROR_NOT_SUPPORTED );
+#endif /* PSA_CRYPTO_DRIVER_TEST */
+
+ default:
+ (void)key_type;
+ (void)key_bits;
+ return( PSA_ERROR_INVALID_ARGUMENT );
+ }
+}
+
+psa_status_t psa_driver_wrapper_export_public_key(
+ const psa_key_attributes_t *attributes,
+ const uint8_t *key_buffer, size_t key_buffer_size,
+ uint8_t *data, size_t data_size, size_t *data_length )
+
+{
+
+ psa_status_t status = PSA_ERROR_INVALID_ARGUMENT;
+ psa_key_location_t location = PSA_KEY_LIFETIME_GET_LOCATION(
+ psa_get_key_lifetime( attributes ) );
+
+ /* Try dynamically-registered SE interface first */
+#if defined(MBEDTLS_PSA_CRYPTO_SE_C)
+ const psa_drv_se_t *drv;
+ psa_drv_se_context_t *drv_context;
+
+ if( psa_get_se_driver( psa_get_key_lifetime(attributes), &drv, &drv_context ) )
+ {
+ if( ( drv->key_management == NULL ) ||
+ ( drv->key_management->p_export_public == NULL ) )
+ {
+ return( PSA_ERROR_NOT_SUPPORTED );
+ }
+
+ return( drv->key_management->p_export_public(
+ drv_context,
+ *( (psa_key_slot_number_t *)key_buffer ),
+ data, data_size, data_length ) );
+ }
+#endif /* MBEDTLS_PSA_CRYPTO_SE_C */
+
+ switch( location )
+ {
+ case PSA_KEY_LOCATION_LOCAL_STORAGE:
+ /* Key is stored in the slot in export representation, so
+ * cycle through all known transparent accelerators */
+#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT)
+
+#if (defined(PSA_CRYPTO_DRIVER_TEST) )
+ status = mbedtls_test_transparent_export_public_key
+ (attributes,
+ key_buffer,
+ key_buffer_size,
+ data,
+ data_size,
+ data_length
+ );
+
+ if( status != PSA_ERROR_NOT_SUPPORTED )
+ return( status );
+#endif
+
+#if (defined(MBEDTLS_PSA_P256M_DRIVER_ENABLED) )
+ status = p256_transparent_export_public_key
+ (attributes,
+ key_buffer,
+ key_buffer_size,
+ data,
+ data_size,
+ data_length
+ );
+
+ if( status != PSA_ERROR_NOT_SUPPORTED )
+ return( status );
+#endif
+
+
+#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */
+ /* Fell through, meaning no accelerator supports this operation */
+ return( psa_export_public_key_internal( attributes,
+ key_buffer,
+ key_buffer_size,
+ data,
+ data_size,
+ data_length ) );
+
+ /* Add cases for opaque driver here */
+#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT)
+
+#if (defined(PSA_CRYPTO_DRIVER_TEST) )
+ case 0x7fffff:
+ return( mbedtls_test_opaque_export_public_key
+ (attributes,
+ key_buffer,
+ key_buffer_size,
+ data,
+ data_size,
+ data_length
+ ));
+#endif
+
+
+#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */
+ default:
+ /* Key is declared with a lifetime not known to us */
+ return( status );
+ }
+
+}
+
+psa_status_t psa_driver_wrapper_get_builtin_key(
+ psa_drv_slot_number_t slot_number,
+ psa_key_attributes_t *attributes,
+ uint8_t *key_buffer, size_t key_buffer_size, size_t *key_buffer_length )
+{
+
+ psa_key_location_t location = PSA_KEY_LIFETIME_GET_LOCATION( psa_get_key_lifetime(attributes) );
+ switch( location )
+ {
+#if defined(PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT)
+
+#if (defined(PSA_CRYPTO_DRIVER_TEST) )
+ case 0x7fffff:
+ return( mbedtls_test_opaque_get_builtin_key
+ (slot_number,
+ attributes,
+ key_buffer,
+ key_buffer_size,
+ key_buffer_length
+ ));
+#endif
+
+
+#endif /* PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT */
+ default:
+ (void) slot_number;
+ (void) key_buffer;
+ (void) key_buffer_size;
+ (void) key_buffer_length;
+ return( PSA_ERROR_DOES_NOT_EXIST );
+ }
+
+}
+
+#endif /* MBEDTLS_PSA_CRYPTO_C */
diff --git a/thirdparty/mbedtls/library/psa_crypto_ecp.c b/thirdparty/mbedtls/library/psa_crypto_ecp.c
new file mode 100644
index 0000000000..95baff6a0f
--- /dev/null
+++ b/thirdparty/mbedtls/library/psa_crypto_ecp.c
@@ -0,0 +1,596 @@
+/*
+ * PSA ECP layer on top of Mbed TLS crypto
+ */
+/*
+ * Copyright The Mbed TLS Contributors
+ * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#include "common.h"
+
+#if defined(MBEDTLS_PSA_CRYPTO_C)
+
+#include <psa/crypto.h>
+#include "psa_crypto_core.h"
+#include "psa_crypto_ecp.h"
+#include "psa_crypto_random_impl.h"
+#include "mbedtls/psa_util.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include "mbedtls/platform.h"
+
+#include <mbedtls/ecdsa.h>
+#include <mbedtls/ecdh.h>
+#include <mbedtls/ecp.h>
+#include <mbedtls/error.h>
+
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR_BASIC) || \
+ defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR_IMPORT) || \
+ defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR_EXPORT) || \
+ defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_PUBLIC_KEY) || \
+ defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || \
+ defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) || \
+ defined(MBEDTLS_PSA_BUILTIN_ALG_ECDH)
+/* Helper function to verify if the provided EC's family and key bit size are valid.
+ *
+ * Note: "bits" parameter is used both as input and output and it might be updated
+ * in case provided input value is not multiple of 8 ("sloppy" bits).
+ */
+static int check_ecc_parameters(psa_ecc_family_t family, size_t *bits)
+{
+ switch (family) {
+ case PSA_ECC_FAMILY_SECP_R1:
+ switch (*bits) {
+ case 192:
+ case 224:
+ case 256:
+ case 384:
+ case 521:
+ return PSA_SUCCESS;
+ case 528:
+ *bits = 521;
+ return PSA_SUCCESS;
+ }
+ break;
+
+ case PSA_ECC_FAMILY_BRAINPOOL_P_R1:
+ switch (*bits) {
+ case 256:
+ case 384:
+ case 512:
+ return PSA_SUCCESS;
+ }
+ break;
+
+ case PSA_ECC_FAMILY_MONTGOMERY:
+ switch (*bits) {
+ case 448:
+ case 255:
+ return PSA_SUCCESS;
+ case 256:
+ *bits = 255;
+ return PSA_SUCCESS;
+ }
+ break;
+
+ case PSA_ECC_FAMILY_SECP_K1:
+ switch (*bits) {
+ case 192:
+ /* secp224k1 is not and will not be supported in PSA (#3541). */
+ case 256:
+ return PSA_SUCCESS;
+ }
+ break;
+ }
+
+ return PSA_ERROR_INVALID_ARGUMENT;
+}
+
+psa_status_t mbedtls_psa_ecp_load_representation(
+ psa_key_type_t type, size_t curve_bits,
+ const uint8_t *data, size_t data_length,
+ mbedtls_ecp_keypair **p_ecp)
+{
+ mbedtls_ecp_group_id grp_id = MBEDTLS_ECP_DP_NONE;
+ psa_status_t status;
+ mbedtls_ecp_keypair *ecp = NULL;
+ size_t curve_bytes = data_length;
+ int explicit_bits = (curve_bits != 0);
+
+ if (PSA_KEY_TYPE_IS_PUBLIC_KEY(type) &&
+ PSA_KEY_TYPE_ECC_GET_FAMILY(type) != PSA_ECC_FAMILY_MONTGOMERY) {
+ /* A Weierstrass public key is represented as:
+ * - The byte 0x04;
+ * - `x_P` as a `ceiling(m/8)`-byte string, big-endian;
+ * - `y_P` as a `ceiling(m/8)`-byte string, big-endian.
+ * So its data length is 2m+1 where m is the curve size in bits.
+ */
+ if ((data_length & 1) == 0) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+ curve_bytes = data_length / 2;
+
+ /* Montgomery public keys are represented in compressed format, meaning
+ * their curve_bytes is equal to the amount of input. */
+
+ /* Private keys are represented in uncompressed private random integer
+ * format, meaning their curve_bytes is equal to the amount of input. */
+ }
+
+ if (explicit_bits) {
+ /* With an explicit bit-size, the data must have the matching length. */
+ if (curve_bytes != PSA_BITS_TO_BYTES(curve_bits)) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+ } else {
+ /* We need to infer the bit-size from the data. Since the only
+ * information we have is the length in bytes, the value of curve_bits
+ * at this stage is rounded up to the nearest multiple of 8. */
+ curve_bits = PSA_BYTES_TO_BITS(curve_bytes);
+ }
+
+ /* Allocate and initialize a key representation. */
+ ecp = mbedtls_calloc(1, sizeof(mbedtls_ecp_keypair));
+ if (ecp == NULL) {
+ return PSA_ERROR_INSUFFICIENT_MEMORY;
+ }
+ mbedtls_ecp_keypair_init(ecp);
+
+ status = check_ecc_parameters(PSA_KEY_TYPE_ECC_GET_FAMILY(type), &curve_bits);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ /* Load the group. */
+ grp_id = mbedtls_ecc_group_from_psa(PSA_KEY_TYPE_ECC_GET_FAMILY(type),
+ curve_bits);
+ if (grp_id == MBEDTLS_ECP_DP_NONE) {
+ status = PSA_ERROR_NOT_SUPPORTED;
+ goto exit;
+ }
+
+ status = mbedtls_to_psa_error(
+ mbedtls_ecp_group_load(&ecp->grp, grp_id));
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ /* Load the key material. */
+ if (PSA_KEY_TYPE_IS_PUBLIC_KEY(type)) {
+ /* Load the public value. */
+ status = mbedtls_to_psa_error(
+ mbedtls_ecp_point_read_binary(&ecp->grp, &ecp->Q,
+ data,
+ data_length));
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ /* Check that the point is on the curve. */
+ status = mbedtls_to_psa_error(
+ mbedtls_ecp_check_pubkey(&ecp->grp, &ecp->Q));
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+ } else {
+ /* Load and validate the secret value. */
+ status = mbedtls_to_psa_error(
+ mbedtls_ecp_read_key(ecp->grp.id,
+ ecp,
+ data,
+ data_length));
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+ }
+
+ *p_ecp = ecp;
+exit:
+ if (status != PSA_SUCCESS) {
+ mbedtls_ecp_keypair_free(ecp);
+ mbedtls_free(ecp);
+ }
+
+ return status;
+}
+#endif /* defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR_BASIC) ||
+ * defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR_IMPORT) ||
+ * defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR_EXPORT) ||
+ * defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_PUBLIC_KEY) ||
+ * defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) ||
+ * defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) ||
+ * defined(MBEDTLS_PSA_BUILTIN_ALG_ECDH) */
+
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR_IMPORT) || \
+ defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR_EXPORT) || \
+ defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_PUBLIC_KEY)
+
+psa_status_t mbedtls_psa_ecp_import_key(
+ const psa_key_attributes_t *attributes,
+ const uint8_t *data, size_t data_length,
+ uint8_t *key_buffer, size_t key_buffer_size,
+ size_t *key_buffer_length, size_t *bits)
+{
+ psa_status_t status;
+ mbedtls_ecp_keypair *ecp = NULL;
+
+ /* Parse input */
+ status = mbedtls_psa_ecp_load_representation(attributes->type,
+ attributes->bits,
+ data,
+ data_length,
+ &ecp);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ if (PSA_KEY_TYPE_ECC_GET_FAMILY(attributes->type) ==
+ PSA_ECC_FAMILY_MONTGOMERY) {
+ *bits = ecp->grp.nbits + 1;
+ } else {
+ *bits = ecp->grp.nbits;
+ }
+
+ /* Re-export the data to PSA export format. There is currently no support
+ * for other input formats then the export format, so this is a 1-1
+ * copy operation. */
+ status = mbedtls_psa_ecp_export_key(attributes->type,
+ ecp,
+ key_buffer,
+ key_buffer_size,
+ key_buffer_length);
+exit:
+ /* Always free the PK object (will also free contained ECP context) */
+ mbedtls_ecp_keypair_free(ecp);
+ mbedtls_free(ecp);
+
+ return status;
+}
+
+psa_status_t mbedtls_psa_ecp_export_key(psa_key_type_t type,
+ mbedtls_ecp_keypair *ecp,
+ uint8_t *data,
+ size_t data_size,
+ size_t *data_length)
+{
+ psa_status_t status;
+
+ if (PSA_KEY_TYPE_IS_PUBLIC_KEY(type)) {
+ /* Check whether the public part is loaded */
+ if (mbedtls_ecp_is_zero(&ecp->Q)) {
+ /* Calculate the public key */
+ status = mbedtls_to_psa_error(
+ mbedtls_ecp_mul(&ecp->grp, &ecp->Q, &ecp->d, &ecp->grp.G,
+ mbedtls_psa_get_random,
+ MBEDTLS_PSA_RANDOM_STATE));
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+ }
+
+ status = mbedtls_to_psa_error(
+ mbedtls_ecp_point_write_binary(&ecp->grp, &ecp->Q,
+ MBEDTLS_ECP_PF_UNCOMPRESSED,
+ data_length,
+ data,
+ data_size));
+ if (status != PSA_SUCCESS) {
+ memset(data, 0, data_size);
+ }
+
+ return status;
+ } else {
+ status = mbedtls_to_psa_error(
+ mbedtls_ecp_write_key_ext(ecp, data_length, data, data_size));
+ return status;
+ }
+}
+
+psa_status_t mbedtls_psa_ecp_export_public_key(
+ const psa_key_attributes_t *attributes,
+ const uint8_t *key_buffer, size_t key_buffer_size,
+ uint8_t *data, size_t data_size, size_t *data_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ mbedtls_ecp_keypair *ecp = NULL;
+
+ status = mbedtls_psa_ecp_load_representation(
+ attributes->type, attributes->bits,
+ key_buffer, key_buffer_size, &ecp);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ status = mbedtls_psa_ecp_export_key(
+ PSA_KEY_TYPE_ECC_PUBLIC_KEY(
+ PSA_KEY_TYPE_ECC_GET_FAMILY(attributes->type)),
+ ecp, data, data_size, data_length);
+
+ mbedtls_ecp_keypair_free(ecp);
+ mbedtls_free(ecp);
+
+ return status;
+}
+#endif /* defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR_IMPORT) ||
+ * defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR_EXPORT) ||
+ * defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_PUBLIC_KEY) */
+
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR_GENERATE)
+psa_status_t mbedtls_psa_ecp_generate_key(
+ const psa_key_attributes_t *attributes,
+ uint8_t *key_buffer, size_t key_buffer_size, size_t *key_buffer_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+
+ psa_ecc_family_t curve = PSA_KEY_TYPE_ECC_GET_FAMILY(
+ attributes->type);
+ mbedtls_ecp_group_id grp_id =
+ mbedtls_ecc_group_from_psa(curve, attributes->bits);
+
+ const mbedtls_ecp_curve_info *curve_info =
+ mbedtls_ecp_curve_info_from_grp_id(grp_id);
+ mbedtls_ecp_keypair ecp;
+
+ if (grp_id == MBEDTLS_ECP_DP_NONE || curve_info == NULL) {
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+
+ mbedtls_ecp_keypair_init(&ecp);
+ ret = mbedtls_ecp_gen_key(grp_id, &ecp,
+ mbedtls_psa_get_random,
+ MBEDTLS_PSA_RANDOM_STATE);
+ if (ret != 0) {
+ mbedtls_ecp_keypair_free(&ecp);
+ return mbedtls_to_psa_error(ret);
+ }
+
+ status = mbedtls_to_psa_error(
+ mbedtls_ecp_write_key_ext(&ecp, key_buffer_length,
+ key_buffer, key_buffer_size));
+
+ mbedtls_ecp_keypair_free(&ecp);
+
+ return status;
+}
+#endif /* MBEDTLS_PSA_BUILTIN_KEY_TYPE_ECC_KEY_PAIR_GENERATE */
+
+/****************************************************************/
+/* ECDSA sign/verify */
+/****************************************************************/
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || \
+ defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA)
+psa_status_t mbedtls_psa_ecdsa_sign_hash(
+ const psa_key_attributes_t *attributes,
+ const uint8_t *key_buffer, size_t key_buffer_size,
+ psa_algorithm_t alg, const uint8_t *hash, size_t hash_length,
+ uint8_t *signature, size_t signature_size, size_t *signature_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ mbedtls_ecp_keypair *ecp = NULL;
+ int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+ size_t curve_bytes;
+ mbedtls_mpi r, s;
+
+ status = mbedtls_psa_ecp_load_representation(attributes->type,
+ attributes->bits,
+ key_buffer,
+ key_buffer_size,
+ &ecp);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ curve_bytes = PSA_BITS_TO_BYTES(ecp->grp.pbits);
+ mbedtls_mpi_init(&r);
+ mbedtls_mpi_init(&s);
+
+ if (signature_size < 2 * curve_bytes) {
+ ret = MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL;
+ goto cleanup;
+ }
+
+ if (PSA_ALG_ECDSA_IS_DETERMINISTIC(alg)) {
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA)
+ psa_algorithm_t hash_alg = PSA_ALG_SIGN_GET_HASH(alg);
+ mbedtls_md_type_t md_alg = mbedtls_md_type_from_psa_alg(hash_alg);
+ MBEDTLS_MPI_CHK(mbedtls_ecdsa_sign_det_ext(
+ &ecp->grp, &r, &s,
+ &ecp->d, hash,
+ hash_length, md_alg,
+ mbedtls_psa_get_random,
+ MBEDTLS_PSA_RANDOM_STATE));
+#else
+ ret = MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE;
+ goto cleanup;
+#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) */
+ } else {
+ (void) alg;
+ MBEDTLS_MPI_CHK(mbedtls_ecdsa_sign(&ecp->grp, &r, &s, &ecp->d,
+ hash, hash_length,
+ mbedtls_psa_get_random,
+ MBEDTLS_PSA_RANDOM_STATE));
+ }
+
+ MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary(&r,
+ signature,
+ curve_bytes));
+ MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary(&s,
+ signature + curve_bytes,
+ curve_bytes));
+cleanup:
+ mbedtls_mpi_free(&r);
+ mbedtls_mpi_free(&s);
+ if (ret == 0) {
+ *signature_length = 2 * curve_bytes;
+ }
+
+ mbedtls_ecp_keypair_free(ecp);
+ mbedtls_free(ecp);
+
+ return mbedtls_to_psa_error(ret);
+}
+
+psa_status_t mbedtls_psa_ecp_load_public_part(mbedtls_ecp_keypair *ecp)
+{
+ int ret = 0;
+
+ /* Check whether the public part is loaded. If not, load it. */
+ if (mbedtls_ecp_is_zero(&ecp->Q)) {
+ ret = mbedtls_ecp_mul(&ecp->grp, &ecp->Q,
+ &ecp->d, &ecp->grp.G,
+ mbedtls_psa_get_random,
+ MBEDTLS_PSA_RANDOM_STATE);
+ }
+
+ return mbedtls_to_psa_error(ret);
+}
+
+psa_status_t mbedtls_psa_ecdsa_verify_hash(
+ const psa_key_attributes_t *attributes,
+ const uint8_t *key_buffer, size_t key_buffer_size,
+ psa_algorithm_t alg, const uint8_t *hash, size_t hash_length,
+ const uint8_t *signature, size_t signature_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ mbedtls_ecp_keypair *ecp = NULL;
+ size_t curve_bytes;
+ mbedtls_mpi r, s;
+
+ (void) alg;
+
+ status = mbedtls_psa_ecp_load_representation(attributes->type,
+ attributes->bits,
+ key_buffer,
+ key_buffer_size,
+ &ecp);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ curve_bytes = PSA_BITS_TO_BYTES(ecp->grp.pbits);
+ mbedtls_mpi_init(&r);
+ mbedtls_mpi_init(&s);
+
+ if (signature_length != 2 * curve_bytes) {
+ status = PSA_ERROR_INVALID_SIGNATURE;
+ goto cleanup;
+ }
+
+ status = mbedtls_to_psa_error(mbedtls_mpi_read_binary(&r,
+ signature,
+ curve_bytes));
+ if (status != PSA_SUCCESS) {
+ goto cleanup;
+ }
+
+ status = mbedtls_to_psa_error(mbedtls_mpi_read_binary(&s,
+ signature + curve_bytes,
+ curve_bytes));
+ if (status != PSA_SUCCESS) {
+ goto cleanup;
+ }
+
+ status = mbedtls_psa_ecp_load_public_part(ecp);
+ if (status != PSA_SUCCESS) {
+ goto cleanup;
+ }
+
+ status = mbedtls_to_psa_error(mbedtls_ecdsa_verify(&ecp->grp, hash,
+ hash_length, &ecp->Q,
+ &r, &s));
+cleanup:
+ mbedtls_mpi_free(&r);
+ mbedtls_mpi_free(&s);
+ mbedtls_ecp_keypair_free(ecp);
+ mbedtls_free(ecp);
+
+ return status;
+}
+
+#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_ECDSA) || \
+ * defined(MBEDTLS_PSA_BUILTIN_ALG_DETERMINISTIC_ECDSA) */
+
+/****************************************************************/
+/* ECDH Key Agreement */
+/****************************************************************/
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_ECDH)
+psa_status_t mbedtls_psa_key_agreement_ecdh(
+ const psa_key_attributes_t *attributes,
+ const uint8_t *key_buffer, size_t key_buffer_size,
+ psa_algorithm_t alg, const uint8_t *peer_key, size_t peer_key_length,
+ uint8_t *shared_secret, size_t shared_secret_size,
+ size_t *shared_secret_length)
+{
+ psa_status_t status;
+ if (!PSA_KEY_TYPE_IS_ECC_KEY_PAIR(attributes->type) ||
+ !PSA_ALG_IS_ECDH(alg)) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+ mbedtls_ecp_keypair *ecp = NULL;
+ status = mbedtls_psa_ecp_load_representation(
+ attributes->type,
+ attributes->bits,
+ key_buffer,
+ key_buffer_size,
+ &ecp);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+ mbedtls_ecp_keypair *their_key = NULL;
+ mbedtls_ecdh_context ecdh;
+ size_t bits = 0;
+ psa_ecc_family_t curve = mbedtls_ecc_group_to_psa(ecp->grp.id, &bits);
+ mbedtls_ecdh_init(&ecdh);
+
+ status = mbedtls_psa_ecp_load_representation(
+ PSA_KEY_TYPE_ECC_PUBLIC_KEY(curve),
+ bits,
+ peer_key,
+ peer_key_length,
+ &their_key);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ status = mbedtls_to_psa_error(
+ mbedtls_ecdh_get_params(&ecdh, their_key, MBEDTLS_ECDH_THEIRS));
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+ status = mbedtls_to_psa_error(
+ mbedtls_ecdh_get_params(&ecdh, ecp, MBEDTLS_ECDH_OURS));
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ status = mbedtls_to_psa_error(
+ mbedtls_ecdh_calc_secret(&ecdh,
+ shared_secret_length,
+ shared_secret, shared_secret_size,
+ mbedtls_psa_get_random,
+ MBEDTLS_PSA_RANDOM_STATE));
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+ if (PSA_BITS_TO_BYTES(bits) != *shared_secret_length) {
+ status = PSA_ERROR_CORRUPTION_DETECTED;
+ }
+exit:
+ if (status != PSA_SUCCESS) {
+ mbedtls_platform_zeroize(shared_secret, shared_secret_size);
+ }
+ mbedtls_ecdh_free(&ecdh);
+ mbedtls_ecp_keypair_free(their_key);
+ mbedtls_free(their_key);
+ mbedtls_ecp_keypair_free(ecp);
+ mbedtls_free(ecp);
+ return status;
+}
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_ECDH */
+
+
+#endif /* MBEDTLS_PSA_CRYPTO_C */
diff --git a/thirdparty/mbedtls/library/psa_crypto_ffdh.c b/thirdparty/mbedtls/library/psa_crypto_ffdh.c
new file mode 100644
index 0000000000..ae38f6d7c6
--- /dev/null
+++ b/thirdparty/mbedtls/library/psa_crypto_ffdh.c
@@ -0,0 +1,321 @@
+/*
+ * PSA FFDH layer on top of Mbed TLS crypto
+ */
+/*
+ * Copyright The Mbed TLS Contributors
+ * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#include "common.h"
+
+#if defined(MBEDTLS_PSA_CRYPTO_C)
+
+/* This header is only needed because it defines
+ * MBEDTLS_DHM_RFC7919_FFDHEXXXX_[P|G]_BIN symbols that are used in
+ * mbedtls_psa_ffdh_set_prime_generator(). Apart from that, this module
+ * only uses bignum functions for arithmetic. */
+#include <mbedtls/dhm.h>
+
+#include <psa/crypto.h>
+#include "psa_crypto_core.h"
+#include "psa_crypto_ffdh.h"
+#include "psa_crypto_random_impl.h"
+#include "mbedtls/platform.h"
+#include "mbedtls/error.h"
+
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_KEY_PAIR_EXPORT) || \
+ defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_KEY_PAIR_GENERATE) || \
+ defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_PUBLIC_KEY) || \
+ defined(MBEDTLS_PSA_BUILTIN_ALG_FFDH)
+static psa_status_t mbedtls_psa_ffdh_set_prime_generator(size_t key_size,
+ mbedtls_mpi *P,
+ mbedtls_mpi *G)
+{
+ const unsigned char *dhm_P = NULL;
+ const unsigned char *dhm_G = NULL;
+ size_t dhm_size_P = 0;
+ size_t dhm_size_G = 0;
+ int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+
+ if (P == NULL && G == NULL) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+#if defined(MBEDTLS_PSA_BUILTIN_DH_RFC7919_2048)
+ static const unsigned char dhm_P_2048[] =
+ MBEDTLS_DHM_RFC7919_FFDHE2048_P_BIN;
+ static const unsigned char dhm_G_2048[] =
+ MBEDTLS_DHM_RFC7919_FFDHE2048_G_BIN;
+#endif /* MBEDTLS_PSA_BUILTIN_DH_RFC7919_2048 */
+#if defined(MBEDTLS_PSA_BUILTIN_DH_RFC7919_3072)
+ static const unsigned char dhm_P_3072[] =
+ MBEDTLS_DHM_RFC7919_FFDHE3072_P_BIN;
+ static const unsigned char dhm_G_3072[] =
+ MBEDTLS_DHM_RFC7919_FFDHE3072_G_BIN;
+#endif /* MBEDTLS_PSA_BUILTIN_DH_RFC7919_3072 */
+#if defined(MBEDTLS_PSA_BUILTIN_DH_RFC7919_4096)
+ static const unsigned char dhm_P_4096[] =
+ MBEDTLS_DHM_RFC7919_FFDHE4096_P_BIN;
+ static const unsigned char dhm_G_4096[] =
+ MBEDTLS_DHM_RFC7919_FFDHE4096_G_BIN;
+#endif /* MBEDTLS_PSA_BUILTIN_DH_RFC7919_4096 */
+#if defined(MBEDTLS_PSA_BUILTIN_DH_RFC7919_6144)
+ static const unsigned char dhm_P_6144[] =
+ MBEDTLS_DHM_RFC7919_FFDHE6144_P_BIN;
+ static const unsigned char dhm_G_6144[] =
+ MBEDTLS_DHM_RFC7919_FFDHE6144_G_BIN;
+#endif /* MBEDTLS_PSA_BUILTIN_DH_RFC7919_6144 */
+#if defined(MBEDTLS_PSA_BUILTIN_DH_RFC7919_8192)
+ static const unsigned char dhm_P_8192[] =
+ MBEDTLS_DHM_RFC7919_FFDHE8192_P_BIN;
+ static const unsigned char dhm_G_8192[] =
+ MBEDTLS_DHM_RFC7919_FFDHE8192_G_BIN;
+#endif /* MBEDTLS_PSA_BUILTIN_DH_RFC7919_8192 */
+
+ switch (key_size) {
+#if defined(MBEDTLS_PSA_BUILTIN_DH_RFC7919_2048)
+ case sizeof(dhm_P_2048):
+ dhm_P = dhm_P_2048;
+ dhm_G = dhm_G_2048;
+ dhm_size_P = sizeof(dhm_P_2048);
+ dhm_size_G = sizeof(dhm_G_2048);
+ break;
+#endif /* MBEDTLS_PSA_BUILTIN_DH_RFC7919_2048 */
+#if defined(MBEDTLS_PSA_BUILTIN_DH_RFC7919_3072)
+ case sizeof(dhm_P_3072):
+ dhm_P = dhm_P_3072;
+ dhm_G = dhm_G_3072;
+ dhm_size_P = sizeof(dhm_P_3072);
+ dhm_size_G = sizeof(dhm_G_3072);
+ break;
+#endif /* MBEDTLS_PSA_BUILTIN_DH_RFC7919_3072 */
+#if defined(MBEDTLS_PSA_BUILTIN_DH_RFC7919_4096)
+ case sizeof(dhm_P_4096):
+ dhm_P = dhm_P_4096;
+ dhm_G = dhm_G_4096;
+ dhm_size_P = sizeof(dhm_P_4096);
+ dhm_size_G = sizeof(dhm_G_4096);
+ break;
+#endif /* MBEDTLS_PSA_BUILTIN_DH_RFC7919_4096 */
+#if defined(MBEDTLS_PSA_BUILTIN_DH_RFC7919_6144)
+ case sizeof(dhm_P_6144):
+ dhm_P = dhm_P_6144;
+ dhm_G = dhm_G_6144;
+ dhm_size_P = sizeof(dhm_P_6144);
+ dhm_size_G = sizeof(dhm_G_6144);
+ break;
+#endif /* MBEDTLS_PSA_BUILTIN_DH_RFC7919_6144 */
+#if defined(MBEDTLS_PSA_BUILTIN_DH_RFC7919_8192)
+ case sizeof(dhm_P_8192):
+ dhm_P = dhm_P_8192;
+ dhm_G = dhm_G_8192;
+ dhm_size_P = sizeof(dhm_P_8192);
+ dhm_size_G = sizeof(dhm_G_8192);
+ break;
+#endif /* MBEDTLS_PSA_BUILTIN_DH_RFC7919_8192 */
+ default:
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ if (P != NULL) {
+ MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(P, dhm_P,
+ dhm_size_P));
+ }
+ if (G != NULL) {
+ MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(G, dhm_G,
+ dhm_size_G));
+ }
+
+cleanup:
+ if (ret != 0) {
+ return mbedtls_to_psa_error(ret);
+ }
+
+ return PSA_SUCCESS;
+}
+#endif /* MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_KEY_PAIR_EXPORT ||
+ MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_KEY_PAIR_GENERATE ||
+ MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_PUBLIC_KEY ||
+ MBEDTLS_PSA_BUILTIN_ALG_FFDH */
+
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_KEY_PAIR_EXPORT) || \
+ defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_PUBLIC_KEY)
+psa_status_t mbedtls_psa_ffdh_export_public_key(
+ const psa_key_attributes_t *attributes,
+ const uint8_t *key_buffer,
+ size_t key_buffer_size,
+ uint8_t *data,
+ size_t data_size,
+ size_t *data_length)
+{
+ int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ mbedtls_mpi GX, G, X, P;
+ psa_key_type_t type = attributes->type;
+
+ if (PSA_KEY_TYPE_IS_PUBLIC_KEY(type)) {
+ if (key_buffer_size > data_size) {
+ return PSA_ERROR_BUFFER_TOO_SMALL;
+ }
+ memcpy(data, key_buffer, key_buffer_size);
+ memset(data + key_buffer_size, 0,
+ data_size - key_buffer_size);
+ *data_length = key_buffer_size;
+ return PSA_SUCCESS;
+ }
+
+ mbedtls_mpi_init(&GX); mbedtls_mpi_init(&G);
+ mbedtls_mpi_init(&X); mbedtls_mpi_init(&P);
+
+ size_t key_len = PSA_BITS_TO_BYTES(attributes->bits);
+
+ status = mbedtls_psa_ffdh_set_prime_generator(key_len, &P, &G);
+
+ if (status != PSA_SUCCESS) {
+ goto cleanup;
+ }
+
+ MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(&X, key_buffer,
+ key_buffer_size));
+
+ MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&GX, &G, &X, &P, NULL));
+ MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary(&GX, data, key_len));
+
+ *data_length = key_len;
+
+ ret = 0;
+cleanup:
+ mbedtls_mpi_free(&P); mbedtls_mpi_free(&G);
+ mbedtls_mpi_free(&X); mbedtls_mpi_free(&GX);
+
+ if (status == PSA_SUCCESS && ret != 0) {
+ status = mbedtls_to_psa_error(ret);
+ }
+
+ return status;
+}
+#endif /* MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_KEY_PAIR_EXPORT ||
+ MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_PUBLIC_KEY */
+
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_KEY_PAIR_GENERATE)
+psa_status_t mbedtls_psa_ffdh_generate_key(
+ const psa_key_attributes_t *attributes,
+ uint8_t *key_buffer, size_t key_buffer_size, size_t *key_buffer_length)
+{
+ mbedtls_mpi X, P;
+ int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ mbedtls_mpi_init(&P); mbedtls_mpi_init(&X);
+ (void) attributes;
+
+ status = mbedtls_psa_ffdh_set_prime_generator(key_buffer_size, &P, NULL);
+
+ if (status != PSA_SUCCESS) {
+ goto cleanup;
+ }
+
+ /* RFC7919: Traditional finite field Diffie-Hellman has each peer choose their
+ secret exponent from the range [2, P-2].
+ Select random value in range [3, P-1] and decrease it by 1. */
+ MBEDTLS_MPI_CHK(mbedtls_mpi_random(&X, 3, &P, mbedtls_psa_get_random,
+ MBEDTLS_PSA_RANDOM_STATE));
+ MBEDTLS_MPI_CHK(mbedtls_mpi_sub_int(&X, &X, 1));
+ MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary(&X, key_buffer, key_buffer_size));
+ *key_buffer_length = key_buffer_size;
+
+cleanup:
+ mbedtls_mpi_free(&P); mbedtls_mpi_free(&X);
+ if (status == PSA_SUCCESS && ret != 0) {
+ return mbedtls_to_psa_error(ret);
+ }
+
+ return status;
+}
+#endif /* MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_KEY_PAIR_GENERATE */
+
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_KEY_PAIR_IMPORT)
+psa_status_t mbedtls_psa_ffdh_import_key(
+ const psa_key_attributes_t *attributes,
+ const uint8_t *data, size_t data_length,
+ uint8_t *key_buffer, size_t key_buffer_size,
+ size_t *key_buffer_length, size_t *bits)
+{
+ (void) attributes;
+
+ if (key_buffer_size < data_length) {
+ return PSA_ERROR_BUFFER_TOO_SMALL;
+ }
+ memcpy(key_buffer, data, data_length);
+ *key_buffer_length = data_length;
+ *bits = PSA_BYTES_TO_BITS(data_length);
+
+ return PSA_SUCCESS;
+}
+#endif /* MBEDTLS_PSA_BUILTIN_KEY_TYPE_DH_KEY_PAIR_IMPORT */
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_FFDH)
+psa_status_t mbedtls_psa_ffdh_key_agreement(
+ const psa_key_attributes_t *attributes,
+ const uint8_t *peer_key,
+ size_t peer_key_length,
+ const uint8_t *key_buffer,
+ size_t key_buffer_size,
+ uint8_t *shared_secret,
+ size_t shared_secret_size,
+ size_t *shared_secret_length)
+{
+ int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ mbedtls_mpi P, G, X, GY, K;
+ const size_t calculated_shared_secret_size = peer_key_length;
+
+ if (peer_key_length != key_buffer_size ||
+ calculated_shared_secret_size > shared_secret_size) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ if (!PSA_KEY_TYPE_IS_DH_KEY_PAIR(psa_get_key_type(attributes))) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ mbedtls_mpi_init(&P); mbedtls_mpi_init(&G);
+ mbedtls_mpi_init(&X); mbedtls_mpi_init(&GY);
+ mbedtls_mpi_init(&K);
+
+ status = mbedtls_psa_ffdh_set_prime_generator(
+ PSA_BITS_TO_BYTES(attributes->bits), &P, &G);
+
+ if (status != PSA_SUCCESS) {
+ goto cleanup;
+ }
+
+ MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(&X, key_buffer,
+ key_buffer_size));
+
+ MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(&GY, peer_key,
+ peer_key_length));
+
+ /* Calculate shared secret public key: K = G^(XY) mod P = GY^X mod P */
+ MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&K, &GY, &X, &P, NULL));
+
+ MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary(&K, shared_secret,
+ calculated_shared_secret_size));
+
+ *shared_secret_length = calculated_shared_secret_size;
+
+ ret = 0;
+
+cleanup:
+ mbedtls_mpi_free(&P); mbedtls_mpi_free(&G);
+ mbedtls_mpi_free(&X); mbedtls_mpi_free(&GY);
+ mbedtls_mpi_free(&K);
+
+ if (status == PSA_SUCCESS && ret != 0) {
+ status = mbedtls_to_psa_error(ret);
+ }
+
+ return status;
+}
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_FFDH */
+
+#endif /* MBEDTLS_PSA_CRYPTO_C */
diff --git a/thirdparty/mbedtls/library/psa_crypto_hash.c b/thirdparty/mbedtls/library/psa_crypto_hash.c
new file mode 100644
index 0000000000..eeb7666c1c
--- /dev/null
+++ b/thirdparty/mbedtls/library/psa_crypto_hash.c
@@ -0,0 +1,470 @@
+/*
+ * PSA hashing layer on top of Mbed TLS software crypto
+ */
+/*
+ * Copyright The Mbed TLS Contributors
+ * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#include "common.h"
+
+#if defined(MBEDTLS_PSA_CRYPTO_C)
+
+#include <psa/crypto.h>
+#include "psa_crypto_core.h"
+#include "psa_crypto_hash.h"
+
+#include <mbedtls/error.h>
+#include <string.h>
+
+#if defined(MBEDTLS_PSA_BUILTIN_HASH)
+psa_status_t mbedtls_psa_hash_abort(
+ mbedtls_psa_hash_operation_t *operation)
+{
+ switch (operation->alg) {
+ case 0:
+ /* The object has (apparently) been initialized but it is not
+ * in use. It's ok to call abort on such an object, and there's
+ * nothing to do. */
+ break;
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_MD5)
+ case PSA_ALG_MD5:
+ mbedtls_md5_free(&operation->ctx.md5);
+ break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_RIPEMD160)
+ case PSA_ALG_RIPEMD160:
+ mbedtls_ripemd160_free(&operation->ctx.ripemd160);
+ break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_1)
+ case PSA_ALG_SHA_1:
+ mbedtls_sha1_free(&operation->ctx.sha1);
+ break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_224)
+ case PSA_ALG_SHA_224:
+ mbedtls_sha256_free(&operation->ctx.sha256);
+ break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_256)
+ case PSA_ALG_SHA_256:
+ mbedtls_sha256_free(&operation->ctx.sha256);
+ break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_384)
+ case PSA_ALG_SHA_384:
+ mbedtls_sha512_free(&operation->ctx.sha512);
+ break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_512)
+ case PSA_ALG_SHA_512:
+ mbedtls_sha512_free(&operation->ctx.sha512);
+ break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_224)
+ case PSA_ALG_SHA3_224:
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_256)
+ case PSA_ALG_SHA3_256:
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_384)
+ case PSA_ALG_SHA3_384:
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_512)
+ case PSA_ALG_SHA3_512:
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_224) || \
+ defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_256) || \
+ defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_384) || \
+ defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_512)
+ mbedtls_sha3_free(&operation->ctx.sha3);
+ break;
+#endif
+ default:
+ return PSA_ERROR_BAD_STATE;
+ }
+ operation->alg = 0;
+ return PSA_SUCCESS;
+}
+
+psa_status_t mbedtls_psa_hash_setup(
+ mbedtls_psa_hash_operation_t *operation,
+ psa_algorithm_t alg)
+{
+ int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+
+ /* A context must be freshly initialized before it can be set up. */
+ if (operation->alg != 0) {
+ return PSA_ERROR_BAD_STATE;
+ }
+
+ switch (alg) {
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_MD5)
+ case PSA_ALG_MD5:
+ mbedtls_md5_init(&operation->ctx.md5);
+ ret = mbedtls_md5_starts(&operation->ctx.md5);
+ break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_RIPEMD160)
+ case PSA_ALG_RIPEMD160:
+ mbedtls_ripemd160_init(&operation->ctx.ripemd160);
+ ret = mbedtls_ripemd160_starts(&operation->ctx.ripemd160);
+ break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_1)
+ case PSA_ALG_SHA_1:
+ mbedtls_sha1_init(&operation->ctx.sha1);
+ ret = mbedtls_sha1_starts(&operation->ctx.sha1);
+ break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_224)
+ case PSA_ALG_SHA_224:
+ mbedtls_sha256_init(&operation->ctx.sha256);
+ ret = mbedtls_sha256_starts(&operation->ctx.sha256, 1);
+ break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_256)
+ case PSA_ALG_SHA_256:
+ mbedtls_sha256_init(&operation->ctx.sha256);
+ ret = mbedtls_sha256_starts(&operation->ctx.sha256, 0);
+ break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_384)
+ case PSA_ALG_SHA_384:
+ mbedtls_sha512_init(&operation->ctx.sha512);
+ ret = mbedtls_sha512_starts(&operation->ctx.sha512, 1);
+ break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_512)
+ case PSA_ALG_SHA_512:
+ mbedtls_sha512_init(&operation->ctx.sha512);
+ ret = mbedtls_sha512_starts(&operation->ctx.sha512, 0);
+ break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_224)
+ case PSA_ALG_SHA3_224:
+ mbedtls_sha3_init(&operation->ctx.sha3);
+ ret = mbedtls_sha3_starts(&operation->ctx.sha3, MBEDTLS_SHA3_224);
+ break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_256)
+ case PSA_ALG_SHA3_256:
+ mbedtls_sha3_init(&operation->ctx.sha3);
+ ret = mbedtls_sha3_starts(&operation->ctx.sha3, MBEDTLS_SHA3_256);
+ break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_384)
+ case PSA_ALG_SHA3_384:
+ mbedtls_sha3_init(&operation->ctx.sha3);
+ ret = mbedtls_sha3_starts(&operation->ctx.sha3, MBEDTLS_SHA3_384);
+ break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_512)
+ case PSA_ALG_SHA3_512:
+ mbedtls_sha3_init(&operation->ctx.sha3);
+ ret = mbedtls_sha3_starts(&operation->ctx.sha3, MBEDTLS_SHA3_512);
+ break;
+#endif
+ default:
+ return PSA_ALG_IS_HASH(alg) ?
+ PSA_ERROR_NOT_SUPPORTED :
+ PSA_ERROR_INVALID_ARGUMENT;
+ }
+ if (ret == 0) {
+ operation->alg = alg;
+ } else {
+ mbedtls_psa_hash_abort(operation);
+ }
+ return mbedtls_to_psa_error(ret);
+}
+
+psa_status_t mbedtls_psa_hash_clone(
+ const mbedtls_psa_hash_operation_t *source_operation,
+ mbedtls_psa_hash_operation_t *target_operation)
+{
+ switch (source_operation->alg) {
+ case 0:
+ return PSA_ERROR_BAD_STATE;
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_MD5)
+ case PSA_ALG_MD5:
+ mbedtls_md5_clone(&target_operation->ctx.md5,
+ &source_operation->ctx.md5);
+ break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_RIPEMD160)
+ case PSA_ALG_RIPEMD160:
+ mbedtls_ripemd160_clone(&target_operation->ctx.ripemd160,
+ &source_operation->ctx.ripemd160);
+ break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_1)
+ case PSA_ALG_SHA_1:
+ mbedtls_sha1_clone(&target_operation->ctx.sha1,
+ &source_operation->ctx.sha1);
+ break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_224)
+ case PSA_ALG_SHA_224:
+ mbedtls_sha256_clone(&target_operation->ctx.sha256,
+ &source_operation->ctx.sha256);
+ break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_256)
+ case PSA_ALG_SHA_256:
+ mbedtls_sha256_clone(&target_operation->ctx.sha256,
+ &source_operation->ctx.sha256);
+ break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_384)
+ case PSA_ALG_SHA_384:
+ mbedtls_sha512_clone(&target_operation->ctx.sha512,
+ &source_operation->ctx.sha512);
+ break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_512)
+ case PSA_ALG_SHA_512:
+ mbedtls_sha512_clone(&target_operation->ctx.sha512,
+ &source_operation->ctx.sha512);
+ break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_224)
+ case PSA_ALG_SHA3_224:
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_256)
+ case PSA_ALG_SHA3_256:
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_384)
+ case PSA_ALG_SHA3_384:
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_512)
+ case PSA_ALG_SHA3_512:
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_224) || \
+ defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_256) || \
+ defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_384) || \
+ defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_512)
+ mbedtls_sha3_clone(&target_operation->ctx.sha3,
+ &source_operation->ctx.sha3);
+ break;
+#endif
+ default:
+ (void) source_operation;
+ (void) target_operation;
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+
+ target_operation->alg = source_operation->alg;
+ return PSA_SUCCESS;
+}
+
+psa_status_t mbedtls_psa_hash_update(
+ mbedtls_psa_hash_operation_t *operation,
+ const uint8_t *input,
+ size_t input_length)
+{
+ int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+
+ switch (operation->alg) {
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_MD5)
+ case PSA_ALG_MD5:
+ ret = mbedtls_md5_update(&operation->ctx.md5,
+ input, input_length);
+ break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_RIPEMD160)
+ case PSA_ALG_RIPEMD160:
+ ret = mbedtls_ripemd160_update(&operation->ctx.ripemd160,
+ input, input_length);
+ break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_1)
+ case PSA_ALG_SHA_1:
+ ret = mbedtls_sha1_update(&operation->ctx.sha1,
+ input, input_length);
+ break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_224)
+ case PSA_ALG_SHA_224:
+ ret = mbedtls_sha256_update(&operation->ctx.sha256,
+ input, input_length);
+ break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_256)
+ case PSA_ALG_SHA_256:
+ ret = mbedtls_sha256_update(&operation->ctx.sha256,
+ input, input_length);
+ break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_384)
+ case PSA_ALG_SHA_384:
+ ret = mbedtls_sha512_update(&operation->ctx.sha512,
+ input, input_length);
+ break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_512)
+ case PSA_ALG_SHA_512:
+ ret = mbedtls_sha512_update(&operation->ctx.sha512,
+ input, input_length);
+ break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_224)
+ case PSA_ALG_SHA3_224:
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_256)
+ case PSA_ALG_SHA3_256:
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_384)
+ case PSA_ALG_SHA3_384:
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_512)
+ case PSA_ALG_SHA3_512:
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_224) || \
+ defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_256) || \
+ defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_384) || \
+ defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_512)
+ ret = mbedtls_sha3_update(&operation->ctx.sha3,
+ input, input_length);
+ break;
+#endif
+ default:
+ (void) input;
+ (void) input_length;
+ return PSA_ERROR_BAD_STATE;
+ }
+
+ return mbedtls_to_psa_error(ret);
+}
+
+psa_status_t mbedtls_psa_hash_finish(
+ mbedtls_psa_hash_operation_t *operation,
+ uint8_t *hash,
+ size_t hash_size,
+ size_t *hash_length)
+{
+ psa_status_t status;
+ int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+ size_t actual_hash_length = PSA_HASH_LENGTH(operation->alg);
+
+ /* Fill the output buffer with something that isn't a valid hash
+ * (barring an attack on the hash and deliberately-crafted input),
+ * in case the caller doesn't check the return status properly. */
+ *hash_length = hash_size;
+ /* If hash_size is 0 then hash may be NULL and then the
+ * call to memset would have undefined behavior. */
+ if (hash_size != 0) {
+ memset(hash, '!', hash_size);
+ }
+
+ if (hash_size < actual_hash_length) {
+ status = PSA_ERROR_BUFFER_TOO_SMALL;
+ goto exit;
+ }
+
+ switch (operation->alg) {
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_MD5)
+ case PSA_ALG_MD5:
+ ret = mbedtls_md5_finish(&operation->ctx.md5, hash);
+ break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_RIPEMD160)
+ case PSA_ALG_RIPEMD160:
+ ret = mbedtls_ripemd160_finish(&operation->ctx.ripemd160, hash);
+ break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_1)
+ case PSA_ALG_SHA_1:
+ ret = mbedtls_sha1_finish(&operation->ctx.sha1, hash);
+ break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_224)
+ case PSA_ALG_SHA_224:
+ ret = mbedtls_sha256_finish(&operation->ctx.sha256, hash);
+ break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_256)
+ case PSA_ALG_SHA_256:
+ ret = mbedtls_sha256_finish(&operation->ctx.sha256, hash);
+ break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_384)
+ case PSA_ALG_SHA_384:
+ ret = mbedtls_sha512_finish(&operation->ctx.sha512, hash);
+ break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA_512)
+ case PSA_ALG_SHA_512:
+ ret = mbedtls_sha512_finish(&operation->ctx.sha512, hash);
+ break;
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_224)
+ case PSA_ALG_SHA3_224:
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_256)
+ case PSA_ALG_SHA3_256:
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_384)
+ case PSA_ALG_SHA3_384:
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_512)
+ case PSA_ALG_SHA3_512:
+#endif
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_224) || \
+ defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_256) || \
+ defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_384) || \
+ defined(MBEDTLS_PSA_BUILTIN_ALG_SHA3_512)
+ ret = mbedtls_sha3_finish(&operation->ctx.sha3, hash, hash_size);
+ break;
+#endif
+ default:
+ (void) hash;
+ return PSA_ERROR_BAD_STATE;
+ }
+ status = mbedtls_to_psa_error(ret);
+
+exit:
+ if (status == PSA_SUCCESS) {
+ *hash_length = actual_hash_length;
+ }
+ return status;
+}
+
+psa_status_t mbedtls_psa_hash_compute(
+ psa_algorithm_t alg,
+ const uint8_t *input,
+ size_t input_length,
+ uint8_t *hash,
+ size_t hash_size,
+ size_t *hash_length)
+{
+ mbedtls_psa_hash_operation_t operation = MBEDTLS_PSA_HASH_OPERATION_INIT;
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_status_t abort_status = PSA_ERROR_CORRUPTION_DETECTED;
+
+ *hash_length = hash_size;
+ status = mbedtls_psa_hash_setup(&operation, alg);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+ status = mbedtls_psa_hash_update(&operation, input, input_length);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+ status = mbedtls_psa_hash_finish(&operation, hash, hash_size, hash_length);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+exit:
+ abort_status = mbedtls_psa_hash_abort(&operation);
+ if (status == PSA_SUCCESS) {
+ return abort_status;
+ } else {
+ return status;
+ }
+
+}
+#endif /* MBEDTLS_PSA_BUILTIN_HASH */
+
+#endif /* MBEDTLS_PSA_CRYPTO_C */
diff --git a/thirdparty/mbedtls/library/psa_crypto_mac.c b/thirdparty/mbedtls/library/psa_crypto_mac.c
new file mode 100644
index 0000000000..8fe6218118
--- /dev/null
+++ b/thirdparty/mbedtls/library/psa_crypto_mac.c
@@ -0,0 +1,496 @@
+/*
+ * PSA MAC layer on top of Mbed TLS software crypto
+ */
+/*
+ * Copyright The Mbed TLS Contributors
+ * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#include "common.h"
+
+#if defined(MBEDTLS_PSA_CRYPTO_C)
+
+#include <psa/crypto.h>
+#include "psa_crypto_core.h"
+#include "psa_crypto_cipher.h"
+#include "psa_crypto_mac.h"
+#include <mbedtls/md.h>
+
+#include <mbedtls/error.h>
+#include "mbedtls/constant_time.h"
+#include <string.h>
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_HMAC)
+static psa_status_t psa_hmac_abort_internal(
+ mbedtls_psa_hmac_operation_t *hmac)
+{
+ mbedtls_platform_zeroize(hmac->opad, sizeof(hmac->opad));
+ return psa_hash_abort(&hmac->hash_ctx);
+}
+
+static psa_status_t psa_hmac_setup_internal(
+ mbedtls_psa_hmac_operation_t *hmac,
+ const uint8_t *key,
+ size_t key_length,
+ psa_algorithm_t hash_alg)
+{
+ uint8_t ipad[PSA_HMAC_MAX_HASH_BLOCK_SIZE];
+ size_t i;
+ size_t hash_size = PSA_HASH_LENGTH(hash_alg);
+ size_t block_size = PSA_HASH_BLOCK_LENGTH(hash_alg);
+ psa_status_t status;
+
+ hmac->alg = hash_alg;
+
+ /* Sanity checks on block_size, to guarantee that there won't be a buffer
+ * overflow below. This should never trigger if the hash algorithm
+ * is implemented correctly. */
+ /* The size checks against the ipad and opad buffers cannot be written
+ * `block_size > sizeof( ipad ) || block_size > sizeof( hmac->opad )`
+ * because that triggers -Wlogical-op on GCC 7.3. */
+ if (block_size > sizeof(ipad)) {
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+ if (block_size > sizeof(hmac->opad)) {
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+ if (block_size < hash_size) {
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+
+ if (key_length > block_size) {
+ status = psa_hash_compute(hash_alg, key, key_length,
+ ipad, sizeof(ipad), &key_length);
+ if (status != PSA_SUCCESS) {
+ goto cleanup;
+ }
+ }
+ /* A 0-length key is not commonly used in HMAC when used as a MAC,
+ * but it is permitted. It is common when HMAC is used in HKDF, for
+ * example. Don't call `memcpy` in the 0-length because `key` could be
+ * an invalid pointer which would make the behavior undefined. */
+ else if (key_length != 0) {
+ memcpy(ipad, key, key_length);
+ }
+
+ /* ipad contains the key followed by garbage. Xor and fill with 0x36
+ * to create the ipad value. */
+ for (i = 0; i < key_length; i++) {
+ ipad[i] ^= 0x36;
+ }
+ memset(ipad + key_length, 0x36, block_size - key_length);
+
+ /* Copy the key material from ipad to opad, flipping the requisite bits,
+ * and filling the rest of opad with the requisite constant. */
+ for (i = 0; i < key_length; i++) {
+ hmac->opad[i] = ipad[i] ^ 0x36 ^ 0x5C;
+ }
+ memset(hmac->opad + key_length, 0x5C, block_size - key_length);
+
+ status = psa_hash_setup(&hmac->hash_ctx, hash_alg);
+ if (status != PSA_SUCCESS) {
+ goto cleanup;
+ }
+
+ status = psa_hash_update(&hmac->hash_ctx, ipad, block_size);
+
+cleanup:
+ mbedtls_platform_zeroize(ipad, sizeof(ipad));
+
+ return status;
+}
+
+static psa_status_t psa_hmac_update_internal(
+ mbedtls_psa_hmac_operation_t *hmac,
+ const uint8_t *data,
+ size_t data_length)
+{
+ return psa_hash_update(&hmac->hash_ctx, data, data_length);
+}
+
+static psa_status_t psa_hmac_finish_internal(
+ mbedtls_psa_hmac_operation_t *hmac,
+ uint8_t *mac,
+ size_t mac_size)
+{
+ uint8_t tmp[PSA_HASH_MAX_SIZE];
+ psa_algorithm_t hash_alg = hmac->alg;
+ size_t hash_size = 0;
+ size_t block_size = PSA_HASH_BLOCK_LENGTH(hash_alg);
+ psa_status_t status;
+
+ status = psa_hash_finish(&hmac->hash_ctx, tmp, sizeof(tmp), &hash_size);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+ /* From here on, tmp needs to be wiped. */
+
+ status = psa_hash_setup(&hmac->hash_ctx, hash_alg);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ status = psa_hash_update(&hmac->hash_ctx, hmac->opad, block_size);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ status = psa_hash_update(&hmac->hash_ctx, tmp, hash_size);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ status = psa_hash_finish(&hmac->hash_ctx, tmp, sizeof(tmp), &hash_size);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ memcpy(mac, tmp, mac_size);
+
+exit:
+ mbedtls_platform_zeroize(tmp, hash_size);
+ return status;
+}
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_HMAC */
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CMAC)
+static psa_status_t cmac_setup(mbedtls_psa_mac_operation_t *operation,
+ const psa_key_attributes_t *attributes,
+ const uint8_t *key_buffer)
+{
+ int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+
+#if defined(PSA_WANT_KEY_TYPE_DES)
+ /* Mbed TLS CMAC does not accept 3DES with only two keys, nor does it accept
+ * to do CMAC with pure DES, so return NOT_SUPPORTED here. */
+ if (psa_get_key_type(attributes) == PSA_KEY_TYPE_DES &&
+ (psa_get_key_bits(attributes) == 64 ||
+ psa_get_key_bits(attributes) == 128)) {
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+#endif
+
+ const mbedtls_cipher_info_t *cipher_info =
+ mbedtls_cipher_info_from_psa(
+ PSA_ALG_CMAC,
+ psa_get_key_type(attributes),
+ psa_get_key_bits(attributes),
+ NULL);
+
+ if (cipher_info == NULL) {
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+
+ ret = mbedtls_cipher_setup(&operation->ctx.cmac, cipher_info);
+ if (ret != 0) {
+ goto exit;
+ }
+
+ ret = mbedtls_cipher_cmac_starts(&operation->ctx.cmac,
+ key_buffer,
+ psa_get_key_bits(attributes));
+exit:
+ return mbedtls_to_psa_error(ret);
+}
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_CMAC */
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_HMAC) || \
+ defined(MBEDTLS_PSA_BUILTIN_ALG_CMAC)
+
+/* Initialize this driver's MAC operation structure. Once this function has been
+ * called, mbedtls_psa_mac_abort can run and will do the right thing. */
+static psa_status_t mac_init(
+ mbedtls_psa_mac_operation_t *operation,
+ psa_algorithm_t alg)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+ operation->alg = alg;
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CMAC)
+ if (PSA_ALG_FULL_LENGTH_MAC(operation->alg) == PSA_ALG_CMAC) {
+ mbedtls_cipher_init(&operation->ctx.cmac);
+ status = PSA_SUCCESS;
+ } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_CMAC */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_HMAC)
+ if (PSA_ALG_IS_HMAC(operation->alg)) {
+ /* We'll set up the hash operation later in psa_hmac_setup_internal. */
+ operation->ctx.hmac.alg = 0;
+ status = PSA_SUCCESS;
+ } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_HMAC */
+ {
+ (void) operation;
+ status = PSA_ERROR_NOT_SUPPORTED;
+ }
+
+ if (status != PSA_SUCCESS) {
+ memset(operation, 0, sizeof(*operation));
+ }
+ return status;
+}
+
+psa_status_t mbedtls_psa_mac_abort(mbedtls_psa_mac_operation_t *operation)
+{
+ if (operation->alg == 0) {
+ /* The object has (apparently) been initialized but it is not
+ * in use. It's ok to call abort on such an object, and there's
+ * nothing to do. */
+ return PSA_SUCCESS;
+ } else
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CMAC)
+ if (PSA_ALG_FULL_LENGTH_MAC(operation->alg) == PSA_ALG_CMAC) {
+ mbedtls_cipher_free(&operation->ctx.cmac);
+ } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_CMAC */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_HMAC)
+ if (PSA_ALG_IS_HMAC(operation->alg)) {
+ psa_hmac_abort_internal(&operation->ctx.hmac);
+ } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_HMAC */
+ {
+ /* Sanity check (shouldn't happen: operation->alg should
+ * always have been initialized to a valid value). */
+ goto bad_state;
+ }
+
+ operation->alg = 0;
+
+ return PSA_SUCCESS;
+
+bad_state:
+ /* If abort is called on an uninitialized object, we can't trust
+ * anything. Wipe the object in case it contains confidential data.
+ * This may result in a memory leak if a pointer gets overwritten,
+ * but it's too late to do anything about this. */
+ memset(operation, 0, sizeof(*operation));
+ return PSA_ERROR_BAD_STATE;
+}
+
+static psa_status_t psa_mac_setup(mbedtls_psa_mac_operation_t *operation,
+ const psa_key_attributes_t *attributes,
+ const uint8_t *key_buffer,
+ size_t key_buffer_size,
+ psa_algorithm_t alg)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+ /* A context must be freshly initialized before it can be set up. */
+ if (operation->alg != 0) {
+ return PSA_ERROR_BAD_STATE;
+ }
+
+ status = mac_init(operation, alg);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CMAC)
+ if (PSA_ALG_FULL_LENGTH_MAC(alg) == PSA_ALG_CMAC) {
+ /* Key buffer size for CMAC is dictated by the key bits set on the
+ * attributes, and previously validated by the core on key import. */
+ (void) key_buffer_size;
+ status = cmac_setup(operation, attributes, key_buffer);
+ } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_CMAC */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_HMAC)
+ if (PSA_ALG_IS_HMAC(alg)) {
+ status = psa_hmac_setup_internal(&operation->ctx.hmac,
+ key_buffer,
+ key_buffer_size,
+ PSA_ALG_HMAC_GET_HASH(alg));
+ } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_HMAC */
+ {
+ (void) attributes;
+ (void) key_buffer;
+ (void) key_buffer_size;
+ status = PSA_ERROR_NOT_SUPPORTED;
+ }
+
+ if (status != PSA_SUCCESS) {
+ mbedtls_psa_mac_abort(operation);
+ }
+
+ return status;
+}
+
+psa_status_t mbedtls_psa_mac_sign_setup(
+ mbedtls_psa_mac_operation_t *operation,
+ const psa_key_attributes_t *attributes,
+ const uint8_t *key_buffer,
+ size_t key_buffer_size,
+ psa_algorithm_t alg)
+{
+ return psa_mac_setup(operation, attributes,
+ key_buffer, key_buffer_size, alg);
+}
+
+psa_status_t mbedtls_psa_mac_verify_setup(
+ mbedtls_psa_mac_operation_t *operation,
+ const psa_key_attributes_t *attributes,
+ const uint8_t *key_buffer,
+ size_t key_buffer_size,
+ psa_algorithm_t alg)
+{
+ return psa_mac_setup(operation, attributes,
+ key_buffer, key_buffer_size, alg);
+}
+
+psa_status_t mbedtls_psa_mac_update(
+ mbedtls_psa_mac_operation_t *operation,
+ const uint8_t *input,
+ size_t input_length)
+{
+ if (operation->alg == 0) {
+ return PSA_ERROR_BAD_STATE;
+ }
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CMAC)
+ if (PSA_ALG_FULL_LENGTH_MAC(operation->alg) == PSA_ALG_CMAC) {
+ return mbedtls_to_psa_error(
+ mbedtls_cipher_cmac_update(&operation->ctx.cmac,
+ input, input_length));
+ } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_CMAC */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_HMAC)
+ if (PSA_ALG_IS_HMAC(operation->alg)) {
+ return psa_hmac_update_internal(&operation->ctx.hmac,
+ input, input_length);
+ } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_HMAC */
+ {
+ /* This shouldn't happen if `operation` was initialized by
+ * a setup function. */
+ (void) input;
+ (void) input_length;
+ return PSA_ERROR_BAD_STATE;
+ }
+}
+
+static psa_status_t psa_mac_finish_internal(
+ mbedtls_psa_mac_operation_t *operation,
+ uint8_t *mac, size_t mac_size)
+{
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_CMAC)
+ if (PSA_ALG_FULL_LENGTH_MAC(operation->alg) == PSA_ALG_CMAC) {
+ uint8_t tmp[PSA_BLOCK_CIPHER_BLOCK_MAX_SIZE];
+ int ret = mbedtls_cipher_cmac_finish(&operation->ctx.cmac, tmp);
+ if (ret == 0) {
+ memcpy(mac, tmp, mac_size);
+ }
+ mbedtls_platform_zeroize(tmp, sizeof(tmp));
+ return mbedtls_to_psa_error(ret);
+ } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_CMAC */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_HMAC)
+ if (PSA_ALG_IS_HMAC(operation->alg)) {
+ return psa_hmac_finish_internal(&operation->ctx.hmac,
+ mac, mac_size);
+ } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_HMAC */
+ {
+ /* This shouldn't happen if `operation` was initialized by
+ * a setup function. */
+ (void) operation;
+ (void) mac;
+ (void) mac_size;
+ return PSA_ERROR_BAD_STATE;
+ }
+}
+
+psa_status_t mbedtls_psa_mac_sign_finish(
+ mbedtls_psa_mac_operation_t *operation,
+ uint8_t *mac,
+ size_t mac_size,
+ size_t *mac_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+ if (operation->alg == 0) {
+ return PSA_ERROR_BAD_STATE;
+ }
+
+ status = psa_mac_finish_internal(operation, mac, mac_size);
+ if (status == PSA_SUCCESS) {
+ *mac_length = mac_size;
+ }
+
+ return status;
+}
+
+psa_status_t mbedtls_psa_mac_verify_finish(
+ mbedtls_psa_mac_operation_t *operation,
+ const uint8_t *mac,
+ size_t mac_length)
+{
+ uint8_t actual_mac[PSA_MAC_MAX_SIZE];
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+ if (operation->alg == 0) {
+ return PSA_ERROR_BAD_STATE;
+ }
+
+ /* Consistency check: requested MAC length fits our local buffer */
+ if (mac_length > sizeof(actual_mac)) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ status = psa_mac_finish_internal(operation, actual_mac, mac_length);
+ if (status != PSA_SUCCESS) {
+ goto cleanup;
+ }
+
+ if (mbedtls_ct_memcmp(mac, actual_mac, mac_length) != 0) {
+ status = PSA_ERROR_INVALID_SIGNATURE;
+ }
+
+cleanup:
+ mbedtls_platform_zeroize(actual_mac, sizeof(actual_mac));
+
+ return status;
+}
+
+psa_status_t mbedtls_psa_mac_compute(
+ const psa_key_attributes_t *attributes,
+ const uint8_t *key_buffer,
+ size_t key_buffer_size,
+ psa_algorithm_t alg,
+ const uint8_t *input,
+ size_t input_length,
+ uint8_t *mac,
+ size_t mac_size,
+ size_t *mac_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ mbedtls_psa_mac_operation_t operation = MBEDTLS_PSA_MAC_OPERATION_INIT;
+
+ status = psa_mac_setup(&operation,
+ attributes, key_buffer, key_buffer_size,
+ alg);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ if (input_length > 0) {
+ status = mbedtls_psa_mac_update(&operation, input, input_length);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+ }
+
+ status = psa_mac_finish_internal(&operation, mac, mac_size);
+ if (status == PSA_SUCCESS) {
+ *mac_length = mac_size;
+ }
+
+exit:
+ mbedtls_psa_mac_abort(&operation);
+
+ return status;
+}
+
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_HMAC || MBEDTLS_PSA_BUILTIN_ALG_CMAC */
+
+#endif /* MBEDTLS_PSA_CRYPTO_C */
diff --git a/thirdparty/mbedtls/library/psa_crypto_pake.c b/thirdparty/mbedtls/library/psa_crypto_pake.c
new file mode 100644
index 0000000000..9ac2e8c486
--- /dev/null
+++ b/thirdparty/mbedtls/library/psa_crypto_pake.c
@@ -0,0 +1,571 @@
+/*
+ * PSA PAKE layer on top of Mbed TLS software crypto
+ */
+/*
+ * Copyright The Mbed TLS Contributors
+ * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#include "common.h"
+
+#if defined(MBEDTLS_PSA_CRYPTO_C)
+
+#include <psa/crypto.h>
+#include "psa_crypto_core.h"
+#include "psa_crypto_pake.h"
+#include "psa_crypto_slot_management.h"
+
+#include <mbedtls/ecjpake.h>
+#include "psa_util_internal.h"
+
+#include <mbedtls/platform.h>
+#include <mbedtls/error.h>
+#include <string.h>
+
+/*
+ * State sequence:
+ *
+ * psa_pake_setup()
+ * |
+ * |-- In any order:
+ * | | psa_pake_set_password_key()
+ * | | psa_pake_set_user()
+ * | | psa_pake_set_peer()
+ * | | psa_pake_set_role()
+ * |
+ * |--- In any order: (First round input before or after first round output)
+ * | |
+ * | |------ In Order
+ * | | | psa_pake_output(PSA_PAKE_STEP_KEY_SHARE)
+ * | | | psa_pake_output(PSA_PAKE_STEP_ZK_PUBLIC)
+ * | | | psa_pake_output(PSA_PAKE_STEP_ZK_PROOF)
+ * | | | psa_pake_output(PSA_PAKE_STEP_KEY_SHARE)
+ * | | | psa_pake_output(PSA_PAKE_STEP_ZK_PUBLIC)
+ * | | | psa_pake_output(PSA_PAKE_STEP_ZK_PROOF)
+ * | |
+ * | |------ In Order:
+ * | | psa_pake_input(PSA_PAKE_STEP_KEY_SHARE)
+ * | | psa_pake_input(PSA_PAKE_STEP_ZK_PUBLIC)
+ * | | psa_pake_input(PSA_PAKE_STEP_ZK_PROOF)
+ * | | psa_pake_input(PSA_PAKE_STEP_KEY_SHARE)
+ * | | psa_pake_input(PSA_PAKE_STEP_ZK_PUBLIC)
+ * | | psa_pake_input(PSA_PAKE_STEP_ZK_PROOF)
+ * |
+ * |--- In any order: (Second round input before or after second round output)
+ * | |
+ * | |------ In Order
+ * | | | psa_pake_output(PSA_PAKE_STEP_KEY_SHARE)
+ * | | | psa_pake_output(PSA_PAKE_STEP_ZK_PUBLIC)
+ * | | | psa_pake_output(PSA_PAKE_STEP_ZK_PROOF)
+ * | |
+ * | |------ In Order:
+ * | | psa_pake_input(PSA_PAKE_STEP_KEY_SHARE)
+ * | | psa_pake_input(PSA_PAKE_STEP_ZK_PUBLIC)
+ * | | psa_pake_input(PSA_PAKE_STEP_ZK_PROOF)
+ * |
+ * psa_pake_get_implicit_key()
+ * psa_pake_abort()
+ */
+
+/*
+ * Possible sequence of calls to implementation:
+ *
+ * |--- In any order:
+ * | |
+ * | |------ In Order
+ * | | | mbedtls_psa_pake_output(PSA_JPAKE_X1_STEP_KEY_SHARE)
+ * | | | mbedtls_psa_pake_output(PSA_JPAKE_X1_STEP_ZK_PUBLIC)
+ * | | | mbedtls_psa_pake_output(PSA_JPAKE_X1_STEP_ZK_PROOF)
+ * | | | mbedtls_psa_pake_output(PSA_JPAKE_X2_STEP_KEY_SHARE)
+ * | | | mbedtls_psa_pake_output(PSA_JPAKE_X2_STEP_ZK_PUBLIC)
+ * | | | mbedtls_psa_pake_output(PSA_JPAKE_X2_STEP_ZK_PROOF)
+ * | |
+ * | |------ In Order:
+ * | | mbedtls_psa_pake_input(PSA_JPAKE_X1_STEP_KEY_SHARE)
+ * | | mbedtls_psa_pake_input(PSA_JPAKE_X1_STEP_ZK_PUBLIC)
+ * | | mbedtls_psa_pake_input(PSA_JPAKE_X1_STEP_ZK_PROOF)
+ * | | mbedtls_psa_pake_input(PSA_JPAKE_X2_STEP_KEY_SHARE)
+ * | | mbedtls_psa_pake_input(PSA_JPAKE_X2_STEP_ZK_PUBLIC)
+ * | | mbedtls_psa_pake_input(PSA_JPAKE_X2_STEP_ZK_PROOF)
+ * |
+ * |--- In any order:
+ * | |
+ * | |------ In Order
+ * | | | mbedtls_psa_pake_output(PSA_JPAKE_X2S_STEP_KEY_SHARE)
+ * | | | mbedtls_psa_pake_output(PSA_JPAKE_X2S_STEP_ZK_PUBLIC)
+ * | | | mbedtls_psa_pake_output(PSA_JPAKE_X2S_STEP_ZK_PROOF)
+ * | |
+ * | |------ In Order:
+ * | | mbedtls_psa_pake_input(PSA_JPAKE_X4S_STEP_KEY_SHARE)
+ * | | mbedtls_psa_pake_input(PSA_JPAKE_X4S_STEP_ZK_PUBLIC)
+ * | | mbedtls_psa_pake_input(PSA_JPAKE_X4S_STEP_ZK_PROOF)
+ */
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_JPAKE)
+static psa_status_t mbedtls_ecjpake_to_psa_error(int ret)
+{
+ switch (ret) {
+ case MBEDTLS_ERR_MPI_BAD_INPUT_DATA:
+ case MBEDTLS_ERR_ECP_BAD_INPUT_DATA:
+ case MBEDTLS_ERR_ECP_INVALID_KEY:
+ case MBEDTLS_ERR_ECP_VERIFY_FAILED:
+ return PSA_ERROR_DATA_INVALID;
+ case MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL:
+ case MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL:
+ return PSA_ERROR_BUFFER_TOO_SMALL;
+ case MBEDTLS_ERR_MD_FEATURE_UNAVAILABLE:
+ return PSA_ERROR_NOT_SUPPORTED;
+ case MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED:
+ return PSA_ERROR_CORRUPTION_DETECTED;
+ default:
+ return PSA_ERROR_GENERIC_ERROR;
+ }
+}
+#endif
+
+#if defined(MBEDTLS_PSA_BUILTIN_PAKE)
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_JPAKE)
+static psa_status_t psa_pake_ecjpake_setup(mbedtls_psa_pake_operation_t *operation)
+{
+ int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+
+ mbedtls_ecjpake_init(&operation->ctx.jpake);
+
+ ret = mbedtls_ecjpake_setup(&operation->ctx.jpake,
+ operation->role,
+ MBEDTLS_MD_SHA256,
+ MBEDTLS_ECP_DP_SECP256R1,
+ operation->password,
+ operation->password_len);
+
+ mbedtls_platform_zeroize(operation->password, operation->password_len);
+
+ if (ret != 0) {
+ return mbedtls_ecjpake_to_psa_error(ret);
+ }
+
+ return PSA_SUCCESS;
+}
+#endif
+
+/* The only two JPAKE user/peer identifiers supported in built-in implementation. */
+static const uint8_t jpake_server_id[] = { 's', 'e', 'r', 'v', 'e', 'r' };
+static const uint8_t jpake_client_id[] = { 'c', 'l', 'i', 'e', 'n', 't' };
+
+psa_status_t mbedtls_psa_pake_setup(mbedtls_psa_pake_operation_t *operation,
+ const psa_crypto_driver_pake_inputs_t *inputs)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ size_t user_len = 0, peer_len = 0, password_len = 0;
+ uint8_t *peer = NULL, *user = NULL;
+ size_t actual_user_len = 0, actual_peer_len = 0, actual_password_len = 0;
+ psa_pake_cipher_suite_t cipher_suite = psa_pake_cipher_suite_init();
+
+ status = psa_crypto_driver_pake_get_password_len(inputs, &password_len);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ status = psa_crypto_driver_pake_get_user_len(inputs, &user_len);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ status = psa_crypto_driver_pake_get_peer_len(inputs, &peer_len);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ status = psa_crypto_driver_pake_get_cipher_suite(inputs, &cipher_suite);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ operation->password = mbedtls_calloc(1, password_len);
+ if (operation->password == NULL) {
+ status = PSA_ERROR_INSUFFICIENT_MEMORY;
+ goto error;
+ }
+
+ user = mbedtls_calloc(1, user_len);
+ if (user == NULL) {
+ status = PSA_ERROR_INSUFFICIENT_MEMORY;
+ goto error;
+ }
+
+ peer = mbedtls_calloc(1, peer_len);
+ if (peer == NULL) {
+ status = PSA_ERROR_INSUFFICIENT_MEMORY;
+ goto error;
+ }
+
+ status = psa_crypto_driver_pake_get_password(inputs, operation->password,
+ password_len, &actual_password_len);
+ if (status != PSA_SUCCESS) {
+ goto error;
+ }
+
+ status = psa_crypto_driver_pake_get_user(inputs, user,
+ user_len, &actual_user_len);
+ if (status != PSA_SUCCESS) {
+ goto error;
+ }
+
+ status = psa_crypto_driver_pake_get_peer(inputs, peer,
+ peer_len, &actual_peer_len);
+ if (status != PSA_SUCCESS) {
+ goto error;
+ }
+
+ operation->password_len = actual_password_len;
+ operation->alg = cipher_suite.algorithm;
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_JPAKE)
+ if (cipher_suite.algorithm == PSA_ALG_JPAKE) {
+ if (cipher_suite.type != PSA_PAKE_PRIMITIVE_TYPE_ECC ||
+ cipher_suite.family != PSA_ECC_FAMILY_SECP_R1 ||
+ cipher_suite.bits != 256 ||
+ cipher_suite.hash != PSA_ALG_SHA_256) {
+ status = PSA_ERROR_NOT_SUPPORTED;
+ goto error;
+ }
+
+ const size_t user_peer_len = sizeof(jpake_client_id); // client and server have the same length
+ if (actual_user_len != user_peer_len ||
+ actual_peer_len != user_peer_len) {
+ status = PSA_ERROR_NOT_SUPPORTED;
+ goto error;
+ }
+
+ if (memcmp(user, jpake_client_id, actual_user_len) == 0 &&
+ memcmp(peer, jpake_server_id, actual_peer_len) == 0) {
+ operation->role = MBEDTLS_ECJPAKE_CLIENT;
+ } else
+ if (memcmp(user, jpake_server_id, actual_user_len) == 0 &&
+ memcmp(peer, jpake_client_id, actual_peer_len) == 0) {
+ operation->role = MBEDTLS_ECJPAKE_SERVER;
+ } else {
+ status = PSA_ERROR_NOT_SUPPORTED;
+ goto error;
+ }
+
+ operation->buffer_length = 0;
+ operation->buffer_offset = 0;
+
+ status = psa_pake_ecjpake_setup(operation);
+ if (status != PSA_SUCCESS) {
+ goto error;
+ }
+
+ /* Role has been set, release user/peer buffers. */
+ mbedtls_free(user); mbedtls_free(peer);
+
+ return PSA_SUCCESS;
+ } else
+#else
+ (void) operation;
+ (void) inputs;
+#endif
+ { status = PSA_ERROR_NOT_SUPPORTED; }
+
+error:
+ mbedtls_free(user); mbedtls_free(peer);
+ /* In case of failure of the setup of a multipart operation, the PSA driver interface
+ * specifies that the core does not call any other driver entry point thus does not
+ * call mbedtls_psa_pake_abort(). Therefore call it here to do the needed clean
+ * up like freeing the memory that may have been allocated to store the password.
+ */
+ mbedtls_psa_pake_abort(operation);
+ return status;
+}
+
+static psa_status_t mbedtls_psa_pake_output_internal(
+ mbedtls_psa_pake_operation_t *operation,
+ psa_crypto_driver_pake_step_t step,
+ uint8_t *output,
+ size_t output_size,
+ size_t *output_length)
+{
+ int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+ size_t length;
+ (void) step; // Unused parameter
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_JPAKE)
+ /*
+ * The PSA CRYPTO PAKE and Mbed TLS JPAKE API have a different
+ * handling of output sequencing.
+ *
+ * The Mbed TLS JPAKE API outputs the whole X1+X2 and X2S steps data
+ * at once, on the other side the PSA CRYPTO PAKE api requires
+ * the KEY_SHARE/ZP_PUBLIC/ZK_PROOF parts of X1, X2 & X2S to be
+ * retrieved in sequence.
+ *
+ * In order to achieve API compatibility, the whole X1+X2 or X2S steps
+ * data is stored in an intermediate buffer at first step output call,
+ * and data is sliced down by parsing the ECPoint records in order
+ * to return the right parts on each step.
+ */
+ if (operation->alg == PSA_ALG_JPAKE) {
+ /* Initialize & write round on KEY_SHARE sequences */
+ if (step == PSA_JPAKE_X1_STEP_KEY_SHARE) {
+ ret = mbedtls_ecjpake_write_round_one(&operation->ctx.jpake,
+ operation->buffer,
+ sizeof(operation->buffer),
+ &operation->buffer_length,
+ mbedtls_psa_get_random,
+ MBEDTLS_PSA_RANDOM_STATE);
+ if (ret != 0) {
+ return mbedtls_ecjpake_to_psa_error(ret);
+ }
+
+ operation->buffer_offset = 0;
+ } else if (step == PSA_JPAKE_X2S_STEP_KEY_SHARE) {
+ ret = mbedtls_ecjpake_write_round_two(&operation->ctx.jpake,
+ operation->buffer,
+ sizeof(operation->buffer),
+ &operation->buffer_length,
+ mbedtls_psa_get_random,
+ MBEDTLS_PSA_RANDOM_STATE);
+ if (ret != 0) {
+ return mbedtls_ecjpake_to_psa_error(ret);
+ }
+
+ operation->buffer_offset = 0;
+ }
+
+ /*
+ * mbedtls_ecjpake_write_round_xxx() outputs thing in the format
+ * defined by draft-cragie-tls-ecjpake-01 section 7. The summary is
+ * that the data for each step is prepended with a length byte, and
+ * then they're concatenated. Additionally, the server's second round
+ * output is prepended with a 3-bytes ECParameters structure.
+ *
+ * In PSA, we output each step separately, and don't prepend the
+ * output with a length byte, even less a curve identifier, as that
+ * information is already available.
+ */
+ if (step == PSA_JPAKE_X2S_STEP_KEY_SHARE &&
+ operation->role == MBEDTLS_ECJPAKE_SERVER) {
+ /* Skip ECParameters, with is 3 bytes (RFC 8422) */
+ operation->buffer_offset += 3;
+ }
+
+ /* Read the length byte then move past it to the data */
+ length = operation->buffer[operation->buffer_offset];
+ operation->buffer_offset += 1;
+
+ if (operation->buffer_offset + length > operation->buffer_length) {
+ return PSA_ERROR_DATA_CORRUPT;
+ }
+
+ if (output_size < length) {
+ return PSA_ERROR_BUFFER_TOO_SMALL;
+ }
+
+ memcpy(output,
+ operation->buffer + operation->buffer_offset,
+ length);
+ *output_length = length;
+
+ operation->buffer_offset += length;
+
+ /* Reset buffer after ZK_PROOF sequence */
+ if ((step == PSA_JPAKE_X2_STEP_ZK_PROOF) ||
+ (step == PSA_JPAKE_X2S_STEP_ZK_PROOF)) {
+ mbedtls_platform_zeroize(operation->buffer, sizeof(operation->buffer));
+ operation->buffer_length = 0;
+ operation->buffer_offset = 0;
+ }
+
+ return PSA_SUCCESS;
+ } else
+#else
+ (void) step;
+ (void) output;
+ (void) output_size;
+ (void) output_length;
+#endif
+ { return PSA_ERROR_NOT_SUPPORTED; }
+}
+
+psa_status_t mbedtls_psa_pake_output(mbedtls_psa_pake_operation_t *operation,
+ psa_crypto_driver_pake_step_t step,
+ uint8_t *output,
+ size_t output_size,
+ size_t *output_length)
+{
+ psa_status_t status = mbedtls_psa_pake_output_internal(
+ operation, step, output, output_size, output_length);
+
+ return status;
+}
+
+static psa_status_t mbedtls_psa_pake_input_internal(
+ mbedtls_psa_pake_operation_t *operation,
+ psa_crypto_driver_pake_step_t step,
+ const uint8_t *input,
+ size_t input_length)
+{
+ int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+ (void) step; // Unused parameter
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_JPAKE)
+ /*
+ * The PSA CRYPTO PAKE and Mbed TLS JPAKE API have a different
+ * handling of input sequencing.
+ *
+ * The Mbed TLS JPAKE API takes the whole X1+X2 or X4S steps data
+ * at once as input, on the other side the PSA CRYPTO PAKE api requires
+ * the KEY_SHARE/ZP_PUBLIC/ZK_PROOF parts of X1, X2 & X4S to be
+ * given in sequence.
+ *
+ * In order to achieve API compatibility, each X1+X2 or X4S step data
+ * is stored sequentially in an intermediate buffer and given to the
+ * Mbed TLS JPAKE API on the last step.
+ *
+ * This causes any input error to be only detected on the last step.
+ */
+ if (operation->alg == PSA_ALG_JPAKE) {
+ /*
+ * Copy input to local buffer and format it as the Mbed TLS API
+ * expects, i.e. as defined by draft-cragie-tls-ecjpake-01 section 7.
+ * The summary is that the data for each step is prepended with a
+ * length byte, and then they're concatenated. Additionally, the
+ * server's second round output is prepended with a 3-bytes
+ * ECParameters structure - which means we have to prepend that when
+ * we're a client.
+ */
+ if (step == PSA_JPAKE_X4S_STEP_KEY_SHARE &&
+ operation->role == MBEDTLS_ECJPAKE_CLIENT) {
+ /* We only support secp256r1. */
+ /* This is the ECParameters structure defined by RFC 8422. */
+ unsigned char ecparameters[3] = {
+ 3, /* named_curve */
+ 0, 23 /* secp256r1 */
+ };
+
+ if (operation->buffer_length + sizeof(ecparameters) >
+ sizeof(operation->buffer)) {
+ return PSA_ERROR_BUFFER_TOO_SMALL;
+ }
+
+ memcpy(operation->buffer + operation->buffer_length,
+ ecparameters, sizeof(ecparameters));
+ operation->buffer_length += sizeof(ecparameters);
+ }
+
+ /*
+ * The core checks that input_length is smaller than
+ * PSA_PAKE_INPUT_MAX_SIZE.
+ * Thus no risk of integer overflow here.
+ */
+ if (operation->buffer_length + input_length + 1 > sizeof(operation->buffer)) {
+ return PSA_ERROR_BUFFER_TOO_SMALL;
+ }
+
+ /* Write the length byte */
+ operation->buffer[operation->buffer_length] = (uint8_t) input_length;
+ operation->buffer_length += 1;
+
+ /* Finally copy the data */
+ memcpy(operation->buffer + operation->buffer_length,
+ input, input_length);
+ operation->buffer_length += input_length;
+
+ /* Load buffer at each last round ZK_PROOF */
+ if (step == PSA_JPAKE_X2_STEP_ZK_PROOF) {
+ ret = mbedtls_ecjpake_read_round_one(&operation->ctx.jpake,
+ operation->buffer,
+ operation->buffer_length);
+
+ mbedtls_platform_zeroize(operation->buffer, sizeof(operation->buffer));
+ operation->buffer_length = 0;
+
+ if (ret != 0) {
+ return mbedtls_ecjpake_to_psa_error(ret);
+ }
+ } else if (step == PSA_JPAKE_X4S_STEP_ZK_PROOF) {
+ ret = mbedtls_ecjpake_read_round_two(&operation->ctx.jpake,
+ operation->buffer,
+ operation->buffer_length);
+
+ mbedtls_platform_zeroize(operation->buffer, sizeof(operation->buffer));
+ operation->buffer_length = 0;
+
+ if (ret != 0) {
+ return mbedtls_ecjpake_to_psa_error(ret);
+ }
+ }
+
+ return PSA_SUCCESS;
+ } else
+#else
+ (void) step;
+ (void) input;
+ (void) input_length;
+#endif
+ { return PSA_ERROR_NOT_SUPPORTED; }
+}
+
+psa_status_t mbedtls_psa_pake_input(mbedtls_psa_pake_operation_t *operation,
+ psa_crypto_driver_pake_step_t step,
+ const uint8_t *input,
+ size_t input_length)
+{
+ psa_status_t status = mbedtls_psa_pake_input_internal(
+ operation, step, input, input_length);
+
+ return status;
+}
+
+psa_status_t mbedtls_psa_pake_get_implicit_key(
+ mbedtls_psa_pake_operation_t *operation,
+ uint8_t *output, size_t output_size,
+ size_t *output_length)
+{
+ int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_JPAKE)
+ if (operation->alg == PSA_ALG_JPAKE) {
+ ret = mbedtls_ecjpake_write_shared_key(&operation->ctx.jpake,
+ output,
+ output_size,
+ output_length,
+ mbedtls_psa_get_random,
+ MBEDTLS_PSA_RANDOM_STATE);
+ if (ret != 0) {
+ return mbedtls_ecjpake_to_psa_error(ret);
+ }
+
+ return PSA_SUCCESS;
+ } else
+#else
+ (void) output;
+#endif
+ { return PSA_ERROR_NOT_SUPPORTED; }
+}
+
+psa_status_t mbedtls_psa_pake_abort(mbedtls_psa_pake_operation_t *operation)
+{
+ mbedtls_zeroize_and_free(operation->password, operation->password_len);
+ operation->password = NULL;
+ operation->password_len = 0;
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_JPAKE)
+ if (operation->alg == PSA_ALG_JPAKE) {
+ operation->role = MBEDTLS_ECJPAKE_NONE;
+ mbedtls_platform_zeroize(operation->buffer, sizeof(operation->buffer));
+ operation->buffer_length = 0;
+ operation->buffer_offset = 0;
+ mbedtls_ecjpake_free(&operation->ctx.jpake);
+ }
+#endif
+
+ operation->alg = PSA_ALG_NONE;
+
+ return PSA_SUCCESS;
+}
+
+#endif /* MBEDTLS_PSA_BUILTIN_PAKE */
+
+#endif /* MBEDTLS_PSA_CRYPTO_C */
diff --git a/thirdparty/mbedtls/library/psa_crypto_rsa.c b/thirdparty/mbedtls/library/psa_crypto_rsa.c
new file mode 100644
index 0000000000..38dc3b8edc
--- /dev/null
+++ b/thirdparty/mbedtls/library/psa_crypto_rsa.c
@@ -0,0 +1,705 @@
+/*
+ * PSA RSA layer on top of Mbed TLS crypto
+ */
+/*
+ * Copyright The Mbed TLS Contributors
+ * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#include "common.h"
+
+#if defined(MBEDTLS_PSA_CRYPTO_C)
+
+#include <psa/crypto.h>
+#include "psa/crypto_values.h"
+#include "psa_crypto_core.h"
+#include "psa_crypto_random_impl.h"
+#include "psa_crypto_rsa.h"
+#include "psa_crypto_hash.h"
+#include "mbedtls/psa_util.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include "mbedtls/platform.h"
+
+#include <mbedtls/rsa.h>
+#include <mbedtls/error.h>
+#include "rsa_internal.h"
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_CRYPT) || \
+ defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP) || \
+ defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_SIGN) || \
+ defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PSS) || \
+ defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR_IMPORT) || \
+ defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR_EXPORT) || \
+ defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_PUBLIC_KEY)
+
+/* Mbed TLS doesn't support non-byte-aligned key sizes (i.e. key sizes
+ * that are not a multiple of 8) well. For example, there is only
+ * mbedtls_rsa_get_len(), which returns a number of bytes, and no
+ * way to return the exact bit size of a key.
+ * To keep things simple, reject non-byte-aligned key sizes. */
+static psa_status_t psa_check_rsa_key_byte_aligned(
+ const mbedtls_rsa_context *rsa)
+{
+ mbedtls_mpi n;
+ psa_status_t status;
+ mbedtls_mpi_init(&n);
+ status = mbedtls_to_psa_error(
+ mbedtls_rsa_export(rsa, &n, NULL, NULL, NULL, NULL));
+ if (status == PSA_SUCCESS) {
+ if (mbedtls_mpi_bitlen(&n) % 8 != 0) {
+ status = PSA_ERROR_NOT_SUPPORTED;
+ }
+ }
+ mbedtls_mpi_free(&n);
+ return status;
+}
+
+psa_status_t mbedtls_psa_rsa_load_representation(
+ psa_key_type_t type, const uint8_t *data, size_t data_length,
+ mbedtls_rsa_context **p_rsa)
+{
+ psa_status_t status;
+ size_t bits;
+
+ *p_rsa = mbedtls_calloc(1, sizeof(mbedtls_rsa_context));
+ if (*p_rsa == NULL) {
+ return PSA_ERROR_INSUFFICIENT_MEMORY;
+ }
+ mbedtls_rsa_init(*p_rsa);
+
+ /* Parse the data. */
+ if (PSA_KEY_TYPE_IS_KEY_PAIR(type)) {
+ status = mbedtls_to_psa_error(mbedtls_rsa_parse_key(*p_rsa, data, data_length));
+ } else {
+ status = mbedtls_to_psa_error(mbedtls_rsa_parse_pubkey(*p_rsa, data, data_length));
+ }
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ /* The size of an RSA key doesn't have to be a multiple of 8. Mbed TLS
+ * supports non-byte-aligned key sizes, but not well. For example,
+ * mbedtls_rsa_get_len() returns the key size in bytes, not in bits. */
+ bits = PSA_BYTES_TO_BITS(mbedtls_rsa_get_len(*p_rsa));
+ if (bits > PSA_VENDOR_RSA_MAX_KEY_BITS) {
+ status = PSA_ERROR_NOT_SUPPORTED;
+ goto exit;
+ }
+ status = psa_check_rsa_key_byte_aligned(*p_rsa);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+exit:
+ return status;
+}
+#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_CRYPT) ||
+ * defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP) ||
+ * defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_SIGN) ||
+ * defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PSS) ||
+ * defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR_IMPORT) ||
+ * defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR_EXPORT) ||
+ * defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_PUBLIC_KEY) */
+
+#if (defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR_IMPORT) && \
+ defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR_EXPORT)) || \
+ defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_PUBLIC_KEY)
+psa_status_t mbedtls_psa_rsa_import_key(
+ const psa_key_attributes_t *attributes,
+ const uint8_t *data, size_t data_length,
+ uint8_t *key_buffer, size_t key_buffer_size,
+ size_t *key_buffer_length, size_t *bits)
+{
+ psa_status_t status;
+ mbedtls_rsa_context *rsa = NULL;
+
+ /* Parse input */
+ status = mbedtls_psa_rsa_load_representation(attributes->type,
+ data,
+ data_length,
+ &rsa);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ *bits = (psa_key_bits_t) PSA_BYTES_TO_BITS(mbedtls_rsa_get_len(rsa));
+
+ /* Re-export the data to PSA export format, such that we can store export
+ * representation in the key slot. Export representation in case of RSA is
+ * the smallest representation that's allowed as input, so a straight-up
+ * allocation of the same size as the input buffer will be large enough. */
+ status = mbedtls_psa_rsa_export_key(attributes->type,
+ rsa,
+ key_buffer,
+ key_buffer_size,
+ key_buffer_length);
+exit:
+ /* Always free the RSA object */
+ mbedtls_rsa_free(rsa);
+ mbedtls_free(rsa);
+
+ return status;
+}
+#endif /* (defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR_IMPORT) &&
+ * defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR_EXPORT)) ||
+ * defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_PUBLIC_KEY) */
+
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR_EXPORT) || \
+ defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_PUBLIC_KEY)
+psa_status_t mbedtls_psa_rsa_export_key(psa_key_type_t type,
+ mbedtls_rsa_context *rsa,
+ uint8_t *data,
+ size_t data_size,
+ size_t *data_length)
+{
+ int ret;
+ uint8_t *end = data + data_size;
+
+ /* PSA Crypto API defines the format of an RSA key as a DER-encoded
+ * representation of the non-encrypted PKCS#1 RSAPrivateKey for a
+ * private key and of the RFC3279 RSAPublicKey for a public key. */
+ if (PSA_KEY_TYPE_IS_KEY_PAIR(type)) {
+ ret = mbedtls_rsa_write_key(rsa, data, &end);
+ } else {
+ ret = mbedtls_rsa_write_pubkey(rsa, data, &end);
+ }
+
+ if (ret < 0) {
+ /* Clean up in case pk_write failed halfway through. */
+ memset(data, 0, data_size);
+ return mbedtls_to_psa_error(ret);
+ }
+
+ /* The mbedtls_pk_xxx functions write to the end of the buffer.
+ * Move the data to the beginning and erase remaining data
+ * at the original location. */
+ if (2 * (size_t) ret <= data_size) {
+ memcpy(data, data + data_size - ret, ret);
+ memset(data + data_size - ret, 0, ret);
+ } else if ((size_t) ret < data_size) {
+ memmove(data, data + data_size - ret, ret);
+ memset(data + ret, 0, data_size - ret);
+ }
+
+ *data_length = ret;
+ return PSA_SUCCESS;
+}
+
+psa_status_t mbedtls_psa_rsa_export_public_key(
+ const psa_key_attributes_t *attributes,
+ const uint8_t *key_buffer, size_t key_buffer_size,
+ uint8_t *data, size_t data_size, size_t *data_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ mbedtls_rsa_context *rsa = NULL;
+
+ status = mbedtls_psa_rsa_load_representation(
+ attributes->type, key_buffer, key_buffer_size, &rsa);
+ if (status == PSA_SUCCESS) {
+ status = mbedtls_psa_rsa_export_key(PSA_KEY_TYPE_RSA_PUBLIC_KEY,
+ rsa,
+ data,
+ data_size,
+ data_length);
+ }
+
+ mbedtls_rsa_free(rsa);
+ mbedtls_free(rsa);
+
+ return status;
+}
+#endif /* defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR_EXPORT) ||
+ * defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_PUBLIC_KEY) */
+
+#if defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR_GENERATE)
+static psa_status_t psa_rsa_read_exponent(const uint8_t *e_bytes,
+ size_t e_length,
+ int *exponent)
+{
+ size_t i;
+ uint32_t acc = 0;
+
+ /* Mbed TLS encodes the public exponent as an int. For simplicity, only
+ * support values that fit in a 32-bit integer, which is larger than
+ * int on just about every platform anyway. */
+ if (e_length > sizeof(acc)) {
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+ for (i = 0; i < e_length; i++) {
+ acc = (acc << 8) | e_bytes[i];
+ }
+ if (acc > INT_MAX) {
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+ *exponent = acc;
+ return PSA_SUCCESS;
+}
+
+psa_status_t mbedtls_psa_rsa_generate_key(
+ const psa_key_attributes_t *attributes,
+ const uint8_t *custom_data, size_t custom_data_length,
+ uint8_t *key_buffer, size_t key_buffer_size, size_t *key_buffer_length)
+{
+ psa_status_t status;
+ mbedtls_rsa_context rsa;
+ int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+ int exponent = 65537;
+
+ if (custom_data_length != 0) {
+ status = psa_rsa_read_exponent(custom_data, custom_data_length,
+ &exponent);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+ }
+
+ mbedtls_rsa_init(&rsa);
+ ret = mbedtls_rsa_gen_key(&rsa,
+ mbedtls_psa_get_random,
+ MBEDTLS_PSA_RANDOM_STATE,
+ (unsigned int) attributes->bits,
+ exponent);
+ if (ret != 0) {
+ mbedtls_rsa_free(&rsa);
+ return mbedtls_to_psa_error(ret);
+ }
+
+ status = mbedtls_psa_rsa_export_key(attributes->type,
+ &rsa, key_buffer, key_buffer_size,
+ key_buffer_length);
+ mbedtls_rsa_free(&rsa);
+
+ return status;
+}
+#endif /* defined(MBEDTLS_PSA_BUILTIN_KEY_TYPE_RSA_KEY_PAIR_GENERATE) */
+
+/****************************************************************/
+/* Sign/verify hashes */
+/****************************************************************/
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_SIGN) || \
+ defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PSS)
+
+/* Decode the hash algorithm from alg and store the mbedtls encoding in
+ * md_alg. Verify that the hash length is acceptable. */
+static psa_status_t psa_rsa_decode_md_type(psa_algorithm_t alg,
+ size_t hash_length,
+ mbedtls_md_type_t *md_alg)
+{
+ psa_algorithm_t hash_alg = PSA_ALG_SIGN_GET_HASH(alg);
+ *md_alg = mbedtls_md_type_from_psa_alg(hash_alg);
+
+ /* The Mbed TLS RSA module uses an unsigned int for hash length
+ * parameters. Validate that it fits so that we don't risk an
+ * overflow later. */
+#if SIZE_MAX > UINT_MAX
+ if (hash_length > UINT_MAX) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+#endif
+
+ /* For signatures using a hash, the hash length must be correct. */
+ if (alg != PSA_ALG_RSA_PKCS1V15_SIGN_RAW) {
+ if (*md_alg == MBEDTLS_MD_NONE) {
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+ if (mbedtls_md_get_size_from_type(*md_alg) != hash_length) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+ }
+
+ return PSA_SUCCESS;
+}
+
+psa_status_t mbedtls_psa_rsa_sign_hash(
+ const psa_key_attributes_t *attributes,
+ const uint8_t *key_buffer, size_t key_buffer_size,
+ psa_algorithm_t alg, const uint8_t *hash, size_t hash_length,
+ uint8_t *signature, size_t signature_size, size_t *signature_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ mbedtls_rsa_context *rsa = NULL;
+ int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+ mbedtls_md_type_t md_alg;
+
+ status = mbedtls_psa_rsa_load_representation(attributes->type,
+ key_buffer,
+ key_buffer_size,
+ &rsa);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ status = psa_rsa_decode_md_type(alg, hash_length, &md_alg);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ if (signature_size < mbedtls_rsa_get_len(rsa)) {
+ status = PSA_ERROR_BUFFER_TOO_SMALL;
+ goto exit;
+ }
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_SIGN)
+ if (PSA_ALG_IS_RSA_PKCS1V15_SIGN(alg)) {
+ ret = mbedtls_rsa_set_padding(rsa, MBEDTLS_RSA_PKCS_V15,
+ MBEDTLS_MD_NONE);
+ if (ret == 0) {
+ ret = mbedtls_rsa_pkcs1_sign(rsa,
+ mbedtls_psa_get_random,
+ MBEDTLS_PSA_RANDOM_STATE,
+ md_alg,
+ (unsigned int) hash_length,
+ hash,
+ signature);
+ }
+ } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_SIGN */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PSS)
+ if (PSA_ALG_IS_RSA_PSS(alg)) {
+ ret = mbedtls_rsa_set_padding(rsa, MBEDTLS_RSA_PKCS_V21, md_alg);
+
+ if (ret == 0) {
+ ret = mbedtls_rsa_rsassa_pss_sign(rsa,
+ mbedtls_psa_get_random,
+ MBEDTLS_PSA_RANDOM_STATE,
+ MBEDTLS_MD_NONE,
+ (unsigned int) hash_length,
+ hash,
+ signature);
+ }
+ } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_RSA_PSS */
+ {
+ status = PSA_ERROR_INVALID_ARGUMENT;
+ goto exit;
+ }
+
+ if (ret == 0) {
+ *signature_length = mbedtls_rsa_get_len(rsa);
+ }
+ status = mbedtls_to_psa_error(ret);
+
+exit:
+ mbedtls_rsa_free(rsa);
+ mbedtls_free(rsa);
+
+ return status;
+}
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PSS)
+static int rsa_pss_expected_salt_len(psa_algorithm_t alg,
+ const mbedtls_rsa_context *rsa,
+ size_t hash_length)
+{
+ if (PSA_ALG_IS_RSA_PSS_ANY_SALT(alg)) {
+ return MBEDTLS_RSA_SALT_LEN_ANY;
+ }
+ /* Otherwise: standard salt length, i.e. largest possible salt length
+ * up to the hash length. */
+ int klen = (int) mbedtls_rsa_get_len(rsa); // known to fit
+ int hlen = (int) hash_length; // known to fit
+ int room = klen - 2 - hlen;
+ if (room < 0) {
+ return 0; // there is no valid signature in this case anyway
+ } else if (room > hlen) {
+ return hlen;
+ } else {
+ return room;
+ }
+}
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_RSA_PSS */
+
+psa_status_t mbedtls_psa_rsa_verify_hash(
+ const psa_key_attributes_t *attributes,
+ const uint8_t *key_buffer, size_t key_buffer_size,
+ psa_algorithm_t alg, const uint8_t *hash, size_t hash_length,
+ const uint8_t *signature, size_t signature_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ mbedtls_rsa_context *rsa = NULL;
+ int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+ mbedtls_md_type_t md_alg;
+
+ status = mbedtls_psa_rsa_load_representation(attributes->type,
+ key_buffer,
+ key_buffer_size,
+ &rsa);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ status = psa_rsa_decode_md_type(alg, hash_length, &md_alg);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ if (signature_length != mbedtls_rsa_get_len(rsa)) {
+ status = PSA_ERROR_INVALID_SIGNATURE;
+ goto exit;
+ }
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_SIGN)
+ if (PSA_ALG_IS_RSA_PKCS1V15_SIGN(alg)) {
+ ret = mbedtls_rsa_set_padding(rsa, MBEDTLS_RSA_PKCS_V15,
+ MBEDTLS_MD_NONE);
+ if (ret == 0) {
+ ret = mbedtls_rsa_pkcs1_verify(rsa,
+ md_alg,
+ (unsigned int) hash_length,
+ hash,
+ signature);
+ }
+ } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_SIGN */
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PSS)
+ if (PSA_ALG_IS_RSA_PSS(alg)) {
+ ret = mbedtls_rsa_set_padding(rsa, MBEDTLS_RSA_PKCS_V21, md_alg);
+ if (ret == 0) {
+ int slen = rsa_pss_expected_salt_len(alg, rsa, hash_length);
+ ret = mbedtls_rsa_rsassa_pss_verify_ext(rsa,
+ md_alg,
+ (unsigned) hash_length,
+ hash,
+ md_alg,
+ slen,
+ signature);
+ }
+ } else
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_RSA_PSS */
+ {
+ status = PSA_ERROR_INVALID_ARGUMENT;
+ goto exit;
+ }
+
+ /* Mbed TLS distinguishes "invalid padding" from "valid padding but
+ * the rest of the signature is invalid". This has little use in
+ * practice and PSA doesn't report this distinction. */
+ status = (ret == MBEDTLS_ERR_RSA_INVALID_PADDING) ?
+ PSA_ERROR_INVALID_SIGNATURE :
+ mbedtls_to_psa_error(ret);
+
+exit:
+ mbedtls_rsa_free(rsa);
+ mbedtls_free(rsa);
+
+ return status;
+}
+
+#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_SIGN) ||
+ * defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PSS) */
+
+/****************************************************************/
+/* Asymmetric cryptography */
+/****************************************************************/
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP)
+static int psa_rsa_oaep_set_padding_mode(psa_algorithm_t alg,
+ mbedtls_rsa_context *rsa)
+{
+ psa_algorithm_t hash_alg = PSA_ALG_RSA_OAEP_GET_HASH(alg);
+ mbedtls_md_type_t md_alg = mbedtls_md_type_from_psa_alg(hash_alg);
+
+ /* Just to get the error status right, as rsa_set_padding() doesn't
+ * distinguish between "bad RSA algorithm" and "unknown hash". */
+ if (mbedtls_md_info_from_type(md_alg) == NULL) {
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+
+ return mbedtls_rsa_set_padding(rsa, MBEDTLS_RSA_PKCS_V21, md_alg);
+}
+#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP) */
+
+psa_status_t mbedtls_psa_asymmetric_encrypt(const psa_key_attributes_t *attributes,
+ const uint8_t *key_buffer,
+ size_t key_buffer_size,
+ psa_algorithm_t alg,
+ const uint8_t *input,
+ size_t input_length,
+ const uint8_t *salt,
+ size_t salt_length,
+ uint8_t *output,
+ size_t output_size,
+ size_t *output_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ (void) key_buffer;
+ (void) key_buffer_size;
+ (void) input;
+ (void) input_length;
+ (void) salt;
+ (void) salt_length;
+ (void) output;
+ (void) output_size;
+ (void) output_length;
+
+ if (PSA_KEY_TYPE_IS_RSA(attributes->type)) {
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_CRYPT) || \
+ defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP)
+ mbedtls_rsa_context *rsa = NULL;
+ status = mbedtls_psa_rsa_load_representation(attributes->type,
+ key_buffer,
+ key_buffer_size,
+ &rsa);
+ if (status != PSA_SUCCESS) {
+ goto rsa_exit;
+ }
+
+ if (output_size < mbedtls_rsa_get_len(rsa)) {
+ status = PSA_ERROR_BUFFER_TOO_SMALL;
+ goto rsa_exit;
+ }
+#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_CRYPT) ||
+ * defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP) */
+ if (alg == PSA_ALG_RSA_PKCS1V15_CRYPT) {
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_CRYPT)
+ status = mbedtls_to_psa_error(
+ mbedtls_rsa_pkcs1_encrypt(rsa,
+ mbedtls_psa_get_random,
+ MBEDTLS_PSA_RANDOM_STATE,
+ input_length,
+ input,
+ output));
+#else
+ status = PSA_ERROR_NOT_SUPPORTED;
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_CRYPT */
+ } else
+ if (PSA_ALG_IS_RSA_OAEP(alg)) {
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP)
+ status = mbedtls_to_psa_error(
+ psa_rsa_oaep_set_padding_mode(alg, rsa));
+ if (status != PSA_SUCCESS) {
+ goto rsa_exit;
+ }
+
+ status = mbedtls_to_psa_error(
+ mbedtls_rsa_rsaes_oaep_encrypt(rsa,
+ mbedtls_psa_get_random,
+ MBEDTLS_PSA_RANDOM_STATE,
+ salt, salt_length,
+ input_length,
+ input,
+ output));
+#else
+ status = PSA_ERROR_NOT_SUPPORTED;
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP */
+ } else {
+ status = PSA_ERROR_INVALID_ARGUMENT;
+ }
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_CRYPT) || \
+ defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP)
+rsa_exit:
+ if (status == PSA_SUCCESS) {
+ *output_length = mbedtls_rsa_get_len(rsa);
+ }
+
+ mbedtls_rsa_free(rsa);
+ mbedtls_free(rsa);
+#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_CRYPT) ||
+ * defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP) */
+ } else {
+ status = PSA_ERROR_NOT_SUPPORTED;
+ }
+
+ return status;
+}
+
+psa_status_t mbedtls_psa_asymmetric_decrypt(const psa_key_attributes_t *attributes,
+ const uint8_t *key_buffer,
+ size_t key_buffer_size,
+ psa_algorithm_t alg,
+ const uint8_t *input,
+ size_t input_length,
+ const uint8_t *salt,
+ size_t salt_length,
+ uint8_t *output,
+ size_t output_size,
+ size_t *output_length)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ (void) key_buffer;
+ (void) key_buffer_size;
+ (void) input;
+ (void) input_length;
+ (void) salt;
+ (void) salt_length;
+ (void) output;
+ (void) output_size;
+ (void) output_length;
+
+ *output_length = 0;
+
+ if (attributes->type == PSA_KEY_TYPE_RSA_KEY_PAIR) {
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_CRYPT) || \
+ defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP)
+ mbedtls_rsa_context *rsa = NULL;
+ status = mbedtls_psa_rsa_load_representation(attributes->type,
+ key_buffer,
+ key_buffer_size,
+ &rsa);
+ if (status != PSA_SUCCESS) {
+ goto rsa_exit;
+ }
+
+ if (input_length != mbedtls_rsa_get_len(rsa)) {
+ status = PSA_ERROR_INVALID_ARGUMENT;
+ goto rsa_exit;
+ }
+#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_CRYPT) ||
+ * defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP) */
+
+ if (alg == PSA_ALG_RSA_PKCS1V15_CRYPT) {
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_CRYPT)
+ status = mbedtls_to_psa_error(
+ mbedtls_rsa_pkcs1_decrypt(rsa,
+ mbedtls_psa_get_random,
+ MBEDTLS_PSA_RANDOM_STATE,
+ output_length,
+ input,
+ output,
+ output_size));
+#else
+ status = PSA_ERROR_NOT_SUPPORTED;
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_CRYPT */
+ } else
+ if (PSA_ALG_IS_RSA_OAEP(alg)) {
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP)
+ status = mbedtls_to_psa_error(
+ psa_rsa_oaep_set_padding_mode(alg, rsa));
+ if (status != PSA_SUCCESS) {
+ goto rsa_exit;
+ }
+
+ status = mbedtls_to_psa_error(
+ mbedtls_rsa_rsaes_oaep_decrypt(rsa,
+ mbedtls_psa_get_random,
+ MBEDTLS_PSA_RANDOM_STATE,
+ salt, salt_length,
+ output_length,
+ input,
+ output,
+ output_size));
+#else
+ status = PSA_ERROR_NOT_SUPPORTED;
+#endif /* MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP */
+ } else {
+ status = PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+#if defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_CRYPT) || \
+ defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP)
+rsa_exit:
+ mbedtls_rsa_free(rsa);
+ mbedtls_free(rsa);
+#endif /* defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_PKCS1V15_CRYPT) ||
+ * defined(MBEDTLS_PSA_BUILTIN_ALG_RSA_OAEP) */
+ } else {
+ status = PSA_ERROR_NOT_SUPPORTED;
+ }
+
+ return status;
+}
+
+#endif /* MBEDTLS_PSA_CRYPTO_C */
diff --git a/thirdparty/mbedtls/library/psa_crypto_se.c b/thirdparty/mbedtls/library/psa_crypto_se.c
new file mode 100644
index 0000000000..7a36a4f3a5
--- /dev/null
+++ b/thirdparty/mbedtls/library/psa_crypto_se.c
@@ -0,0 +1,373 @@
+/*
+ * PSA crypto support for secure element drivers
+ */
+/*
+ * Copyright The Mbed TLS Contributors
+ * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#include "common.h"
+
+#if defined(MBEDTLS_PSA_CRYPTO_SE_C)
+
+#include <stdint.h>
+#include <string.h>
+
+#include "psa/crypto_se_driver.h"
+
+#include "psa_crypto_se.h"
+
+#if defined(MBEDTLS_PSA_ITS_FILE_C)
+#include "psa_crypto_its.h"
+#else /* Native ITS implementation */
+#include "psa/error.h"
+#include "psa/internal_trusted_storage.h"
+#endif
+
+#include "mbedtls/platform.h"
+
+
+
+/****************************************************************/
+/* Driver lookup */
+/****************************************************************/
+
+/* This structure is identical to psa_drv_se_context_t declared in
+ * `crypto_se_driver.h`, except that some parts are writable here
+ * (non-const, or pointer to non-const). */
+typedef struct {
+ void *persistent_data;
+ size_t persistent_data_size;
+ uintptr_t transient_data;
+} psa_drv_se_internal_context_t;
+
+struct psa_se_drv_table_entry_s {
+ psa_key_location_t location;
+ const psa_drv_se_t *methods;
+ union {
+ psa_drv_se_internal_context_t internal;
+ psa_drv_se_context_t context;
+ } u;
+};
+
+static psa_se_drv_table_entry_t driver_table[PSA_MAX_SE_DRIVERS];
+
+psa_se_drv_table_entry_t *psa_get_se_driver_entry(
+ psa_key_lifetime_t lifetime)
+{
+ size_t i;
+ psa_key_location_t location = PSA_KEY_LIFETIME_GET_LOCATION(lifetime);
+ /* In the driver table, location=0 means an entry that isn't used.
+ * No driver has a location of 0 because it's a reserved value
+ * (which designates transparent keys). Make sure we never return
+ * a driver entry for location 0. */
+ if (location == 0) {
+ return NULL;
+ }
+ for (i = 0; i < PSA_MAX_SE_DRIVERS; i++) {
+ if (driver_table[i].location == location) {
+ return &driver_table[i];
+ }
+ }
+ return NULL;
+}
+
+const psa_drv_se_t *psa_get_se_driver_methods(
+ const psa_se_drv_table_entry_t *driver)
+{
+ return driver->methods;
+}
+
+psa_drv_se_context_t *psa_get_se_driver_context(
+ psa_se_drv_table_entry_t *driver)
+{
+ return &driver->u.context;
+}
+
+int psa_get_se_driver(psa_key_lifetime_t lifetime,
+ const psa_drv_se_t **p_methods,
+ psa_drv_se_context_t **p_drv_context)
+{
+ psa_se_drv_table_entry_t *driver = psa_get_se_driver_entry(lifetime);
+ if (p_methods != NULL) {
+ *p_methods = (driver ? driver->methods : NULL);
+ }
+ if (p_drv_context != NULL) {
+ *p_drv_context = (driver ? &driver->u.context : NULL);
+ }
+ return driver != NULL;
+}
+
+
+
+/****************************************************************/
+/* Persistent data management */
+/****************************************************************/
+
+static psa_status_t psa_get_se_driver_its_file_uid(
+ const psa_se_drv_table_entry_t *driver,
+ psa_storage_uid_t *uid)
+{
+ if (driver->location > PSA_MAX_SE_LOCATION) {
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+
+ /* ITS file sizes are limited to 32 bits. */
+ if (driver->u.internal.persistent_data_size > UINT32_MAX) {
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+
+ /* See the documentation of PSA_CRYPTO_SE_DRIVER_ITS_UID_BASE. */
+ *uid = PSA_CRYPTO_SE_DRIVER_ITS_UID_BASE + driver->location;
+ return PSA_SUCCESS;
+}
+
+psa_status_t psa_load_se_persistent_data(
+ const psa_se_drv_table_entry_t *driver)
+{
+ psa_status_t status;
+ psa_storage_uid_t uid;
+ size_t length;
+
+ status = psa_get_se_driver_its_file_uid(driver, &uid);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ /* Read the amount of persistent data that the driver requests.
+ * If the data in storage is larger, it is truncated. If the data
+ * in storage is smaller, silently keep what is already at the end
+ * of the output buffer. */
+ /* psa_get_se_driver_its_file_uid ensures that the size_t
+ * persistent_data_size is in range, but compilers don't know that,
+ * so cast to reassure them. */
+ return psa_its_get(uid, 0,
+ (uint32_t) driver->u.internal.persistent_data_size,
+ driver->u.internal.persistent_data,
+ &length);
+}
+
+psa_status_t psa_save_se_persistent_data(
+ const psa_se_drv_table_entry_t *driver)
+{
+ psa_status_t status;
+ psa_storage_uid_t uid;
+
+ status = psa_get_se_driver_its_file_uid(driver, &uid);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ /* psa_get_se_driver_its_file_uid ensures that the size_t
+ * persistent_data_size is in range, but compilers don't know that,
+ * so cast to reassure them. */
+ return psa_its_set(uid,
+ (uint32_t) driver->u.internal.persistent_data_size,
+ driver->u.internal.persistent_data,
+ 0);
+}
+
+psa_status_t psa_destroy_se_persistent_data(psa_key_location_t location)
+{
+ psa_storage_uid_t uid;
+ if (location > PSA_MAX_SE_LOCATION) {
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+ uid = PSA_CRYPTO_SE_DRIVER_ITS_UID_BASE + location;
+ return psa_its_remove(uid);
+}
+
+psa_status_t psa_find_se_slot_for_key(
+ const psa_key_attributes_t *attributes,
+ psa_key_creation_method_t method,
+ psa_se_drv_table_entry_t *driver,
+ psa_key_slot_number_t *slot_number)
+{
+ psa_status_t status;
+ psa_key_location_t key_location =
+ PSA_KEY_LIFETIME_GET_LOCATION(psa_get_key_lifetime(attributes));
+
+ /* If the location is wrong, it's a bug in the library. */
+ if (driver->location != key_location) {
+ return PSA_ERROR_CORRUPTION_DETECTED;
+ }
+
+ /* If the driver doesn't support key creation in any way, give up now. */
+ if (driver->methods->key_management == NULL) {
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+
+ if (psa_get_key_slot_number(attributes, slot_number) == PSA_SUCCESS) {
+ /* The application wants to use a specific slot. Allow it if
+ * the driver supports it. On a system with isolation,
+ * the crypto service must check that the application is
+ * permitted to request this slot. */
+ psa_drv_se_validate_slot_number_t p_validate_slot_number =
+ driver->methods->key_management->p_validate_slot_number;
+ if (p_validate_slot_number == NULL) {
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+ status = p_validate_slot_number(&driver->u.context,
+ driver->u.internal.persistent_data,
+ attributes, method,
+ *slot_number);
+ } else if (method == PSA_KEY_CREATION_REGISTER) {
+ /* The application didn't specify a slot number. This doesn't
+ * make sense when registering a slot. */
+ return PSA_ERROR_INVALID_ARGUMENT;
+ } else {
+ /* The application didn't tell us which slot to use. Let the driver
+ * choose. This is the normal case. */
+ psa_drv_se_allocate_key_t p_allocate =
+ driver->methods->key_management->p_allocate;
+ if (p_allocate == NULL) {
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+ status = p_allocate(&driver->u.context,
+ driver->u.internal.persistent_data,
+ attributes, method,
+ slot_number);
+ }
+ return status;
+}
+
+psa_status_t psa_destroy_se_key(psa_se_drv_table_entry_t *driver,
+ psa_key_slot_number_t slot_number)
+{
+ psa_status_t status;
+ psa_status_t storage_status;
+ /* Normally a missing method would mean that the action is not
+ * supported. But psa_destroy_key() is not supposed to return
+ * PSA_ERROR_NOT_SUPPORTED: if you can create a key, you should
+ * be able to destroy it. The only use case for a driver that
+ * does not have a way to destroy keys at all is if the keys are
+ * locked in a read-only state: we can use the keys but not
+ * destroy them. Hence, if the driver doesn't support destroying
+ * keys, it's really a lack of permission. */
+ if (driver->methods->key_management == NULL ||
+ driver->methods->key_management->p_destroy == NULL) {
+ return PSA_ERROR_NOT_PERMITTED;
+ }
+ status = driver->methods->key_management->p_destroy(
+ &driver->u.context,
+ driver->u.internal.persistent_data,
+ slot_number);
+ storage_status = psa_save_se_persistent_data(driver);
+ return status == PSA_SUCCESS ? storage_status : status;
+}
+
+psa_status_t psa_init_all_se_drivers(void)
+{
+ size_t i;
+ for (i = 0; i < PSA_MAX_SE_DRIVERS; i++) {
+ psa_se_drv_table_entry_t *driver = &driver_table[i];
+ if (driver->location == 0) {
+ continue; /* skipping unused entry */
+ }
+ const psa_drv_se_t *methods = psa_get_se_driver_methods(driver);
+ if (methods->p_init != NULL) {
+ psa_status_t status = methods->p_init(
+ &driver->u.context,
+ driver->u.internal.persistent_data,
+ driver->location);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+ status = psa_save_se_persistent_data(driver);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+ }
+ }
+ return PSA_SUCCESS;
+}
+
+
+
+/****************************************************************/
+/* Driver registration */
+/****************************************************************/
+
+psa_status_t psa_register_se_driver(
+ psa_key_location_t location,
+ const psa_drv_se_t *methods)
+{
+ size_t i;
+ psa_status_t status;
+
+ if (methods->hal_version != PSA_DRV_SE_HAL_VERSION) {
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+ /* Driver table entries are 0-initialized. 0 is not a valid driver
+ * location because it means a transparent key. */
+ MBEDTLS_STATIC_ASSERT(PSA_KEY_LOCATION_LOCAL_STORAGE == 0,
+ "Secure element support requires 0 to mean a local key");
+
+ if (location == PSA_KEY_LOCATION_LOCAL_STORAGE) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+ if (location > PSA_MAX_SE_LOCATION) {
+ return PSA_ERROR_NOT_SUPPORTED;
+ }
+
+ for (i = 0; i < PSA_MAX_SE_DRIVERS; i++) {
+ if (driver_table[i].location == 0) {
+ break;
+ }
+ /* Check that location isn't already in use up to the first free
+ * entry. Since entries are created in order and never deleted,
+ * there can't be a used entry after the first free entry. */
+ if (driver_table[i].location == location) {
+ return PSA_ERROR_ALREADY_EXISTS;
+ }
+ }
+ if (i == PSA_MAX_SE_DRIVERS) {
+ return PSA_ERROR_INSUFFICIENT_MEMORY;
+ }
+
+ driver_table[i].location = location;
+ driver_table[i].methods = methods;
+ driver_table[i].u.internal.persistent_data_size =
+ methods->persistent_data_size;
+
+ if (methods->persistent_data_size != 0) {
+ driver_table[i].u.internal.persistent_data =
+ mbedtls_calloc(1, methods->persistent_data_size);
+ if (driver_table[i].u.internal.persistent_data == NULL) {
+ status = PSA_ERROR_INSUFFICIENT_MEMORY;
+ goto error;
+ }
+ /* Load the driver's persistent data. On first use, the persistent
+ * data does not exist in storage, and is initialized to
+ * all-bits-zero by the calloc call just above. */
+ status = psa_load_se_persistent_data(&driver_table[i]);
+ if (status != PSA_SUCCESS && status != PSA_ERROR_DOES_NOT_EXIST) {
+ goto error;
+ }
+ }
+
+ return PSA_SUCCESS;
+
+error:
+ memset(&driver_table[i], 0, sizeof(driver_table[i]));
+ return status;
+}
+
+void psa_unregister_all_se_drivers(void)
+{
+ size_t i;
+ for (i = 0; i < PSA_MAX_SE_DRIVERS; i++) {
+ if (driver_table[i].u.internal.persistent_data != NULL) {
+ mbedtls_free(driver_table[i].u.internal.persistent_data);
+ }
+ }
+ memset(driver_table, 0, sizeof(driver_table));
+}
+
+
+
+/****************************************************************/
+/* The end */
+/****************************************************************/
+
+#endif /* MBEDTLS_PSA_CRYPTO_SE_C */
diff --git a/thirdparty/mbedtls/library/psa_crypto_slot_management.c b/thirdparty/mbedtls/library/psa_crypto_slot_management.c
new file mode 100644
index 0000000000..9850d8c750
--- /dev/null
+++ b/thirdparty/mbedtls/library/psa_crypto_slot_management.c
@@ -0,0 +1,1131 @@
+/*
+ * PSA crypto layer on top of Mbed TLS crypto
+ */
+/*
+ * Copyright The Mbed TLS Contributors
+ * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#include "common.h"
+
+#if defined(MBEDTLS_PSA_CRYPTO_C)
+
+#include "psa/crypto.h"
+
+#include "psa_crypto_core.h"
+#include "psa_crypto_driver_wrappers_no_static.h"
+#include "psa_crypto_slot_management.h"
+#include "psa_crypto_storage.h"
+#if defined(MBEDTLS_PSA_CRYPTO_SE_C)
+#include "psa_crypto_se.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include "mbedtls/platform.h"
+#if defined(MBEDTLS_THREADING_C)
+#include "mbedtls/threading.h"
+#endif
+
+
+
+/* Make sure we have distinct ranges of key identifiers for distinct
+ * purposes. */
+MBEDTLS_STATIC_ASSERT(PSA_KEY_ID_USER_MIN < PSA_KEY_ID_USER_MAX,
+ "Empty user key ID range");
+MBEDTLS_STATIC_ASSERT(PSA_KEY_ID_VENDOR_MIN < PSA_KEY_ID_VENDOR_MAX,
+ "Empty vendor key ID range");
+MBEDTLS_STATIC_ASSERT(MBEDTLS_PSA_KEY_ID_BUILTIN_MIN < MBEDTLS_PSA_KEY_ID_BUILTIN_MAX,
+ "Empty builtin key ID range");
+MBEDTLS_STATIC_ASSERT(PSA_KEY_ID_VOLATILE_MIN < PSA_KEY_ID_VOLATILE_MAX,
+ "Empty volatile key ID range");
+
+MBEDTLS_STATIC_ASSERT(PSA_KEY_ID_USER_MAX < PSA_KEY_ID_VENDOR_MIN ||
+ PSA_KEY_ID_VENDOR_MAX < PSA_KEY_ID_USER_MIN,
+ "Overlap between user key IDs and vendor key IDs");
+
+MBEDTLS_STATIC_ASSERT(PSA_KEY_ID_VENDOR_MIN <= MBEDTLS_PSA_KEY_ID_BUILTIN_MIN &&
+ MBEDTLS_PSA_KEY_ID_BUILTIN_MAX <= PSA_KEY_ID_VENDOR_MAX,
+ "Builtin key identifiers are not in the vendor range");
+
+MBEDTLS_STATIC_ASSERT(PSA_KEY_ID_VENDOR_MIN <= PSA_KEY_ID_VOLATILE_MIN &&
+ PSA_KEY_ID_VOLATILE_MAX <= PSA_KEY_ID_VENDOR_MAX,
+ "Volatile key identifiers are not in the vendor range");
+
+MBEDTLS_STATIC_ASSERT(PSA_KEY_ID_VOLATILE_MAX < MBEDTLS_PSA_KEY_ID_BUILTIN_MIN ||
+ MBEDTLS_PSA_KEY_ID_BUILTIN_MAX < PSA_KEY_ID_VOLATILE_MIN,
+ "Overlap between builtin key IDs and volatile key IDs");
+
+
+
+#if defined(MBEDTLS_PSA_KEY_STORE_DYNAMIC)
+
+/* Dynamic key store.
+ *
+ * The key store consists of multiple slices.
+ *
+ * The volatile keys are stored in variable-sized tables called slices.
+ * Slices are allocated on demand and deallocated when possible.
+ * The size of slices increases exponentially, so the average overhead
+ * (number of slots that are allocated but not used) is roughly
+ * proportional to the number of keys (with a factor that grows
+ * when the key store is fragmented).
+ *
+ * One slice is dedicated to the cache of persistent and built-in keys.
+ * For simplicity, they are separated from volatile keys. This cache
+ * slice has a fixed size and has the slice index KEY_SLOT_CACHE_SLICE_INDEX,
+ * located after the slices for volatile keys.
+ */
+
+/* Size of the last slice containing the cache of persistent and built-in keys. */
+#define PERSISTENT_KEY_CACHE_COUNT MBEDTLS_PSA_KEY_SLOT_COUNT
+
+/* Volatile keys are stored in slices 0 through
+ * (KEY_SLOT_VOLATILE_SLICE_COUNT - 1) inclusive.
+ * Each slice is twice the size of the previous slice.
+ * Volatile key identifiers encode the slice number as follows:
+ * bits 30..31: 0b10 (mandated by the PSA Crypto specification).
+ * bits 25..29: slice index (0...KEY_SLOT_VOLATILE_SLICE_COUNT-1)
+ * bits 0..24: slot index in slice
+ */
+#define KEY_ID_SLOT_INDEX_WIDTH 25u
+#define KEY_ID_SLICE_INDEX_WIDTH 5u
+
+#define KEY_SLOT_VOLATILE_SLICE_BASE_LENGTH 16u
+#define KEY_SLOT_VOLATILE_SLICE_COUNT 22u
+#define KEY_SLICE_COUNT (KEY_SLOT_VOLATILE_SLICE_COUNT + 1u)
+#define KEY_SLOT_CACHE_SLICE_INDEX KEY_SLOT_VOLATILE_SLICE_COUNT
+
+
+/* Check that the length of the largest slice (calculated as
+ * KEY_SLICE_LENGTH_MAX below) does not overflow size_t. We use
+ * an indirect method in case the calculation of KEY_SLICE_LENGTH_MAX
+ * itself overflows uintmax_t: if (BASE_LENGTH << c)
+ * overflows size_t then BASE_LENGTH > SIZE_MAX >> c.
+ */
+#if (KEY_SLOT_VOLATILE_SLICE_BASE_LENGTH > \
+ SIZE_MAX >> (KEY_SLOT_VOLATILE_SLICE_COUNT - 1))
+#error "Maximum slice length overflows size_t"
+#endif
+
+#if KEY_ID_SLICE_INDEX_WIDTH + KEY_ID_SLOT_INDEX_WIDTH > 30
+#error "Not enough room in volatile key IDs for slice index and slot index"
+#endif
+#if KEY_SLOT_VOLATILE_SLICE_COUNT > (1 << KEY_ID_SLICE_INDEX_WIDTH)
+#error "Too many slices to fit the slice index in a volatile key ID"
+#endif
+#define KEY_SLICE_LENGTH_MAX \
+ (KEY_SLOT_VOLATILE_SLICE_BASE_LENGTH << (KEY_SLOT_VOLATILE_SLICE_COUNT - 1))
+#if KEY_SLICE_LENGTH_MAX > 1 << KEY_ID_SLOT_INDEX_WIDTH
+#error "Not enough room in volatile key IDs for a slot index in the largest slice"
+#endif
+#if KEY_ID_SLICE_INDEX_WIDTH > 8
+#error "Slice index does not fit in uint8_t for psa_key_slot_t::slice_index"
+#endif
+
+
+/* Calculate the volatile key id to use for a given slot.
+ * This function assumes valid parameter values. */
+static psa_key_id_t volatile_key_id_of_index(size_t slice_idx,
+ size_t slot_idx)
+{
+ /* We assert above that the slice and slot indexes fit in separate
+ * bit-fields inside psa_key_id_t, which is a 32-bit type per the
+ * PSA Cryptography specification. */
+ return (psa_key_id_t) (0x40000000u |
+ (slice_idx << KEY_ID_SLOT_INDEX_WIDTH) |
+ slot_idx);
+}
+
+/* Calculate the slice containing the given volatile key.
+ * This function assumes valid parameter values. */
+static size_t slice_index_of_volatile_key_id(psa_key_id_t key_id)
+{
+ size_t mask = (1LU << KEY_ID_SLICE_INDEX_WIDTH) - 1;
+ return (key_id >> KEY_ID_SLOT_INDEX_WIDTH) & mask;
+}
+
+/* Calculate the index of the slot containing the given volatile key.
+ * This function assumes valid parameter values. */
+static size_t slot_index_of_volatile_key_id(psa_key_id_t key_id)
+{
+ return key_id & ((1LU << KEY_ID_SLOT_INDEX_WIDTH) - 1);
+}
+
+/* In global_data.first_free_slot_index, use this special value to
+ * indicate that the slice is full. */
+#define FREE_SLOT_INDEX_NONE ((size_t) -1)
+
+#if defined(MBEDTLS_TEST_HOOKS)
+size_t psa_key_slot_volatile_slice_count(void)
+{
+ return KEY_SLOT_VOLATILE_SLICE_COUNT;
+}
+#endif
+
+#else /* MBEDTLS_PSA_KEY_STORE_DYNAMIC */
+
+/* Static key store.
+ *
+ * All the keys (volatile or persistent) are in a single slice.
+ * We only use slices as a concept to allow some differences between
+ * static and dynamic key store management to be buried in auxiliary
+ * functions.
+ */
+
+#define PERSISTENT_KEY_CACHE_COUNT MBEDTLS_PSA_KEY_SLOT_COUNT
+#define KEY_SLICE_COUNT 1u
+#define KEY_SLOT_CACHE_SLICE_INDEX 0
+
+#endif /* MBEDTLS_PSA_KEY_STORE_DYNAMIC */
+
+
+typedef struct {
+#if defined(MBEDTLS_PSA_KEY_STORE_DYNAMIC)
+ psa_key_slot_t *key_slices[KEY_SLICE_COUNT];
+ size_t first_free_slot_index[KEY_SLOT_VOLATILE_SLICE_COUNT];
+#else /* MBEDTLS_PSA_KEY_STORE_DYNAMIC */
+ psa_key_slot_t key_slots[MBEDTLS_PSA_KEY_SLOT_COUNT];
+#endif /* MBEDTLS_PSA_KEY_STORE_DYNAMIC */
+ uint8_t key_slots_initialized;
+} psa_global_data_t;
+
+static psa_global_data_t global_data;
+
+static uint8_t psa_get_key_slots_initialized(void)
+{
+ uint8_t initialized;
+
+#if defined(MBEDTLS_THREADING_C)
+ mbedtls_mutex_lock(&mbedtls_threading_psa_globaldata_mutex);
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+ initialized = global_data.key_slots_initialized;
+
+#if defined(MBEDTLS_THREADING_C)
+ mbedtls_mutex_unlock(&mbedtls_threading_psa_globaldata_mutex);
+#endif /* defined(MBEDTLS_THREADING_C) */
+
+ return initialized;
+}
+
+
+
+/** The length of the given slice in the key slot table.
+ *
+ * \param slice_idx The slice number. It must satisfy
+ * 0 <= slice_idx < KEY_SLICE_COUNT.
+ *
+ * \return The number of elements in the given slice.
+ */
+static inline size_t key_slice_length(size_t slice_idx);
+
+/** Get a pointer to the slot where the given volatile key is located.
+ *
+ * \param key_id The key identifier. It must be a valid volatile key
+ * identifier.
+ * \return A pointer to the only slot that the given key
+ * can be in. Note that the slot may be empty or
+ * contain a different key.
+ */
+static inline psa_key_slot_t *get_volatile_key_slot(psa_key_id_t key_id);
+
+/** Get a pointer to an entry in the persistent key cache.
+ *
+ * \param slot_idx The index in the table. It must satisfy
+ * 0 <= slot_idx < PERSISTENT_KEY_CACHE_COUNT.
+ * \return A pointer to the slot containing the given
+ * persistent key cache entry.
+ */
+static inline psa_key_slot_t *get_persistent_key_slot(size_t slot_idx);
+
+/** Get a pointer to a slot given by slice and index.
+ *
+ * \param slice_idx The slice number. It must satisfy
+ * 0 <= slice_idx < KEY_SLICE_COUNT.
+ * \param slot_idx An index in the given slice. It must satisfy
+ * 0 <= slot_idx < key_slice_length(slice_idx).
+ *
+ * \return A pointer to the given slot.
+ */
+static inline psa_key_slot_t *get_key_slot(size_t slice_idx, size_t slot_idx);
+
+#if defined(MBEDTLS_PSA_KEY_STORE_DYNAMIC)
+
+#if defined(MBEDTLS_TEST_HOOKS)
+size_t (*mbedtls_test_hook_psa_volatile_key_slice_length)(size_t slice_idx) = NULL;
+#endif
+
+static inline size_t key_slice_length(size_t slice_idx)
+{
+ if (slice_idx == KEY_SLOT_CACHE_SLICE_INDEX) {
+ return PERSISTENT_KEY_CACHE_COUNT;
+ } else {
+#if defined(MBEDTLS_TEST_HOOKS)
+ if (mbedtls_test_hook_psa_volatile_key_slice_length != NULL) {
+ return mbedtls_test_hook_psa_volatile_key_slice_length(slice_idx);
+ }
+#endif
+ return KEY_SLOT_VOLATILE_SLICE_BASE_LENGTH << slice_idx;
+ }
+}
+
+static inline psa_key_slot_t *get_volatile_key_slot(psa_key_id_t key_id)
+{
+ size_t slice_idx = slice_index_of_volatile_key_id(key_id);
+ if (slice_idx >= KEY_SLOT_VOLATILE_SLICE_COUNT) {
+ return NULL;
+ }
+ size_t slot_idx = slot_index_of_volatile_key_id(key_id);
+ if (slot_idx >= key_slice_length(slice_idx)) {
+ return NULL;
+ }
+ psa_key_slot_t *slice = global_data.key_slices[slice_idx];
+ if (slice == NULL) {
+ return NULL;
+ }
+ return &slice[slot_idx];
+}
+
+static inline psa_key_slot_t *get_persistent_key_slot(size_t slot_idx)
+{
+ return &global_data.key_slices[KEY_SLOT_CACHE_SLICE_INDEX][slot_idx];
+}
+
+static inline psa_key_slot_t *get_key_slot(size_t slice_idx, size_t slot_idx)
+{
+ return &global_data.key_slices[slice_idx][slot_idx];
+}
+
+#else /* MBEDTLS_PSA_KEY_STORE_DYNAMIC */
+
+static inline size_t key_slice_length(size_t slice_idx)
+{
+ (void) slice_idx;
+ return ARRAY_LENGTH(global_data.key_slots);
+}
+
+static inline psa_key_slot_t *get_volatile_key_slot(psa_key_id_t key_id)
+{
+ MBEDTLS_STATIC_ASSERT(ARRAY_LENGTH(global_data.key_slots) <=
+ PSA_KEY_ID_VOLATILE_MAX - PSA_KEY_ID_VOLATILE_MIN + 1,
+ "The key slot array is larger than the volatile key ID range");
+ return &global_data.key_slots[key_id - PSA_KEY_ID_VOLATILE_MIN];
+}
+
+static inline psa_key_slot_t *get_persistent_key_slot(size_t slot_idx)
+{
+ return &global_data.key_slots[slot_idx];
+}
+
+static inline psa_key_slot_t *get_key_slot(size_t slice_idx, size_t slot_idx)
+{
+ (void) slice_idx;
+ return &global_data.key_slots[slot_idx];
+}
+
+#endif /* MBEDTLS_PSA_KEY_STORE_DYNAMIC */
+
+
+
+int psa_is_valid_key_id(mbedtls_svc_key_id_t key, int vendor_ok)
+{
+ psa_key_id_t key_id = MBEDTLS_SVC_KEY_ID_GET_KEY_ID(key);
+
+ if ((PSA_KEY_ID_USER_MIN <= key_id) &&
+ (key_id <= PSA_KEY_ID_USER_MAX)) {
+ return 1;
+ }
+
+ if (vendor_ok &&
+ (PSA_KEY_ID_VENDOR_MIN <= key_id) &&
+ (key_id <= PSA_KEY_ID_VENDOR_MAX)) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/** Get the description in memory of a key given its identifier and lock it.
+ *
+ * The descriptions of volatile keys and loaded persistent keys are
+ * stored in key slots. This function returns a pointer to the key slot
+ * containing the description of a key given its identifier.
+ *
+ * The function searches the key slots containing the description of the key
+ * with \p key identifier. The function does only read accesses to the key
+ * slots. The function does not load any persistent key thus does not access
+ * any storage.
+ *
+ * For volatile key identifiers, only one key slot is queried as a volatile
+ * key with identifier key_id can only be stored in slot of index
+ * ( key_id - #PSA_KEY_ID_VOLATILE_MIN ).
+ *
+ * On success, the function locks the key slot. It is the responsibility of
+ * the caller to unlock the key slot when it does not access it anymore.
+ *
+ * If multi-threading is enabled, the caller must hold the
+ * global key slot mutex.
+ *
+ * \param key Key identifier to query.
+ * \param[out] p_slot On success, `*p_slot` contains a pointer to the
+ * key slot containing the description of the key
+ * identified by \p key.
+ *
+ * \retval #PSA_SUCCESS
+ * The pointer to the key slot containing the description of the key
+ * identified by \p key was returned.
+ * \retval #PSA_ERROR_INVALID_HANDLE
+ * \p key is not a valid key identifier.
+ * \retval #PSA_ERROR_DOES_NOT_EXIST
+ * There is no key with key identifier \p key in the key slots.
+ */
+static psa_status_t psa_get_and_lock_key_slot_in_memory(
+ mbedtls_svc_key_id_t key, psa_key_slot_t **p_slot)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_key_id_t key_id = MBEDTLS_SVC_KEY_ID_GET_KEY_ID(key);
+ size_t slot_idx;
+ psa_key_slot_t *slot = NULL;
+
+ if (psa_key_id_is_volatile(key_id)) {
+ slot = get_volatile_key_slot(key_id);
+
+ /* Check if both the PSA key identifier key_id and the owner
+ * identifier of key match those of the key slot. */
+ if (slot != NULL &&
+ slot->state == PSA_SLOT_FULL &&
+ mbedtls_svc_key_id_equal(key, slot->attr.id)) {
+ status = PSA_SUCCESS;
+ } else {
+ status = PSA_ERROR_DOES_NOT_EXIST;
+ }
+ } else {
+ if (!psa_is_valid_key_id(key, 1)) {
+ return PSA_ERROR_INVALID_HANDLE;
+ }
+
+ for (slot_idx = 0; slot_idx < PERSISTENT_KEY_CACHE_COUNT; slot_idx++) {
+ slot = get_persistent_key_slot(slot_idx);
+ /* Only consider slots which are in a full state. */
+ if ((slot->state == PSA_SLOT_FULL) &&
+ (mbedtls_svc_key_id_equal(key, slot->attr.id))) {
+ break;
+ }
+ }
+ status = (slot_idx < MBEDTLS_PSA_KEY_SLOT_COUNT) ?
+ PSA_SUCCESS : PSA_ERROR_DOES_NOT_EXIST;
+ }
+
+ if (status == PSA_SUCCESS) {
+ status = psa_register_read(slot);
+ if (status == PSA_SUCCESS) {
+ *p_slot = slot;
+ }
+ }
+
+ return status;
+}
+
+psa_status_t psa_initialize_key_slots(void)
+{
+#if defined(MBEDTLS_PSA_KEY_STORE_DYNAMIC)
+ global_data.key_slices[KEY_SLOT_CACHE_SLICE_INDEX] =
+ mbedtls_calloc(PERSISTENT_KEY_CACHE_COUNT,
+ sizeof(*global_data.key_slices[KEY_SLOT_CACHE_SLICE_INDEX]));
+ if (global_data.key_slices[KEY_SLOT_CACHE_SLICE_INDEX] == NULL) {
+ return PSA_ERROR_INSUFFICIENT_MEMORY;
+ }
+#else /* MBEDTLS_PSA_KEY_STORE_DYNAMIC */
+ /* Nothing to do: program startup and psa_wipe_all_key_slots() both
+ * guarantee that the key slots are initialized to all-zero, which
+ * means that all the key slots are in a valid, empty state. The global
+ * data mutex is already held when calling this function, so no need to
+ * lock it here, to set the flag. */
+#endif /* MBEDTLS_PSA_KEY_STORE_DYNAMIC */
+
+ global_data.key_slots_initialized = 1;
+ return PSA_SUCCESS;
+}
+
+void psa_wipe_all_key_slots(void)
+{
+ for (size_t slice_idx = 0; slice_idx < KEY_SLICE_COUNT; slice_idx++) {
+#if defined(MBEDTLS_PSA_KEY_STORE_DYNAMIC)
+ if (global_data.key_slices[slice_idx] == NULL) {
+ continue;
+ }
+#endif /* MBEDTLS_PSA_KEY_STORE_DYNAMIC */
+ for (size_t slot_idx = 0; slot_idx < key_slice_length(slice_idx); slot_idx++) {
+ psa_key_slot_t *slot = get_key_slot(slice_idx, slot_idx);
+#if defined(MBEDTLS_PSA_KEY_STORE_DYNAMIC)
+ /* When MBEDTLS_PSA_KEY_STORE_DYNAMIC is disabled, calling
+ * psa_wipe_key_slot() on an unused slot is useless, but it
+ * happens to work (because we flip the state to PENDING_DELETION).
+ *
+ * When MBEDTLS_PSA_KEY_STORE_DYNAMIC is enabled,
+ * psa_wipe_key_slot() needs to have a valid slice_index
+ * field, but that value might not be correct in a
+ * free slot, so we must not call it.
+ *
+ * Bypass the call to psa_wipe_key_slot() if the slot is empty,
+ * but only if MBEDTLS_PSA_KEY_STORE_DYNAMIC is enabled, to save
+ * a few bytes of code size otherwise.
+ */
+ if (slot->state == PSA_SLOT_EMPTY) {
+ continue;
+ }
+#endif
+ slot->var.occupied.registered_readers = 1;
+ slot->state = PSA_SLOT_PENDING_DELETION;
+ (void) psa_wipe_key_slot(slot);
+ }
+#if defined(MBEDTLS_PSA_KEY_STORE_DYNAMIC)
+ mbedtls_free(global_data.key_slices[slice_idx]);
+ global_data.key_slices[slice_idx] = NULL;
+#endif /* MBEDTLS_PSA_KEY_STORE_DYNAMIC */
+ }
+
+#if defined(MBEDTLS_PSA_KEY_STORE_DYNAMIC)
+ for (size_t slice_idx = 0; slice_idx < KEY_SLOT_VOLATILE_SLICE_COUNT; slice_idx++) {
+ global_data.first_free_slot_index[slice_idx] = 0;
+ }
+#endif /* MBEDTLS_PSA_KEY_STORE_DYNAMIC */
+
+ /* The global data mutex is already held when calling this function. */
+ global_data.key_slots_initialized = 0;
+}
+
+#if defined(MBEDTLS_PSA_KEY_STORE_DYNAMIC)
+
+static psa_status_t psa_allocate_volatile_key_slot(psa_key_id_t *key_id,
+ psa_key_slot_t **p_slot)
+{
+ size_t slice_idx;
+ for (slice_idx = 0; slice_idx < KEY_SLOT_VOLATILE_SLICE_COUNT; slice_idx++) {
+ if (global_data.first_free_slot_index[slice_idx] != FREE_SLOT_INDEX_NONE) {
+ break;
+ }
+ }
+ if (slice_idx == KEY_SLOT_VOLATILE_SLICE_COUNT) {
+ return PSA_ERROR_INSUFFICIENT_MEMORY;
+ }
+
+ if (global_data.key_slices[slice_idx] == NULL) {
+ global_data.key_slices[slice_idx] =
+ mbedtls_calloc(key_slice_length(slice_idx),
+ sizeof(psa_key_slot_t));
+ if (global_data.key_slices[slice_idx] == NULL) {
+ return PSA_ERROR_INSUFFICIENT_MEMORY;
+ }
+ }
+ psa_key_slot_t *slice = global_data.key_slices[slice_idx];
+
+ size_t slot_idx = global_data.first_free_slot_index[slice_idx];
+ *key_id = volatile_key_id_of_index(slice_idx, slot_idx);
+
+ psa_key_slot_t *slot = &slice[slot_idx];
+ size_t next_free = slot_idx + 1 + slot->var.free.next_free_relative_to_next;
+ if (next_free >= key_slice_length(slice_idx)) {
+ next_free = FREE_SLOT_INDEX_NONE;
+ }
+ global_data.first_free_slot_index[slice_idx] = next_free;
+ /* The .next_free field is not meaningful when the slot is not free,
+ * so give it the same content as freshly initialized memory. */
+ slot->var.free.next_free_relative_to_next = 0;
+
+ psa_status_t status = psa_key_slot_state_transition(slot,
+ PSA_SLOT_EMPTY,
+ PSA_SLOT_FILLING);
+ if (status != PSA_SUCCESS) {
+ /* The only reason for failure is if the slot state was not empty.
+ * This indicates that something has gone horribly wrong.
+ * In this case, we leave the slot out of the free list, and stop
+ * modifying it. This minimizes any further corruption. The slot
+ * is a memory leak, but that's a lesser evil. */
+ return status;
+ }
+
+ *p_slot = slot;
+ /* We assert at compile time that the slice index fits in uint8_t. */
+ slot->slice_index = (uint8_t) slice_idx;
+ return PSA_SUCCESS;
+}
+
+psa_status_t psa_free_key_slot(size_t slice_idx,
+ psa_key_slot_t *slot)
+{
+
+ if (slice_idx == KEY_SLOT_CACHE_SLICE_INDEX) {
+ /* This is a cache entry. We don't maintain a free list, so
+ * there's nothing to do. */
+ return PSA_SUCCESS;
+ }
+ if (slice_idx >= KEY_SLOT_VOLATILE_SLICE_COUNT) {
+ return PSA_ERROR_CORRUPTION_DETECTED;
+ }
+
+ psa_key_slot_t *slice = global_data.key_slices[slice_idx];
+ psa_key_slot_t *slice_end = slice + key_slice_length(slice_idx);
+ if (slot < slice || slot >= slice_end) {
+ /* The slot isn't actually in the slice! We can't detect that
+ * condition for sure, because the pointer comparison itself is
+ * undefined behavior in that case. That same condition makes the
+ * subtraction to calculate the slot index also UB.
+ * Give up now to avoid causing further corruption.
+ */
+ return PSA_ERROR_CORRUPTION_DETECTED;
+ }
+ size_t slot_idx = slot - slice;
+
+ size_t next_free = global_data.first_free_slot_index[slice_idx];
+ if (next_free >= key_slice_length(slice_idx)) {
+ /* The slot was full. The newly freed slot thus becomes the
+ * end of the free list. */
+ next_free = key_slice_length(slice_idx);
+ }
+ global_data.first_free_slot_index[slice_idx] = slot_idx;
+ slot->var.free.next_free_relative_to_next =
+ (int32_t) next_free - (int32_t) slot_idx - 1;
+
+ return PSA_SUCCESS;
+}
+#endif /* MBEDTLS_PSA_KEY_STORE_DYNAMIC */
+
+psa_status_t psa_reserve_free_key_slot(psa_key_id_t *volatile_key_id,
+ psa_key_slot_t **p_slot)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ size_t slot_idx;
+ psa_key_slot_t *selected_slot, *unused_persistent_key_slot;
+
+ if (!psa_get_key_slots_initialized()) {
+ status = PSA_ERROR_BAD_STATE;
+ goto error;
+ }
+
+#if defined(MBEDTLS_PSA_KEY_STORE_DYNAMIC)
+ if (volatile_key_id != NULL) {
+ return psa_allocate_volatile_key_slot(volatile_key_id, p_slot);
+ }
+#endif /* MBEDTLS_PSA_KEY_STORE_DYNAMIC */
+
+ /* With a dynamic key store, allocate an entry in the cache slice,
+ * applicable only to non-volatile keys that get cached in RAM.
+ * With a static key store, allocate an entry in the sole slice,
+ * applicable to all keys. */
+ selected_slot = unused_persistent_key_slot = NULL;
+ for (slot_idx = 0; slot_idx < PERSISTENT_KEY_CACHE_COUNT; slot_idx++) {
+ psa_key_slot_t *slot = get_key_slot(KEY_SLOT_CACHE_SLICE_INDEX, slot_idx);
+ if (slot->state == PSA_SLOT_EMPTY) {
+ selected_slot = slot;
+ break;
+ }
+
+ if ((unused_persistent_key_slot == NULL) &&
+ (slot->state == PSA_SLOT_FULL) &&
+ (!psa_key_slot_has_readers(slot)) &&
+ (!PSA_KEY_LIFETIME_IS_VOLATILE(slot->attr.lifetime))) {
+ unused_persistent_key_slot = slot;
+ }
+ }
+
+ /*
+ * If there is no unused key slot and there is at least one unlocked key
+ * slot containing the description of a persistent key, recycle the first
+ * such key slot we encountered. If we later need to operate on the
+ * persistent key we are evicting now, we will reload its description from
+ * storage.
+ */
+ if ((selected_slot == NULL) &&
+ (unused_persistent_key_slot != NULL)) {
+ selected_slot = unused_persistent_key_slot;
+ psa_register_read(selected_slot);
+ status = psa_wipe_key_slot(selected_slot);
+ if (status != PSA_SUCCESS) {
+ goto error;
+ }
+ }
+
+ if (selected_slot != NULL) {
+ status = psa_key_slot_state_transition(selected_slot, PSA_SLOT_EMPTY,
+ PSA_SLOT_FILLING);
+ if (status != PSA_SUCCESS) {
+ goto error;
+ }
+
+#if defined(MBEDTLS_PSA_KEY_STORE_DYNAMIC)
+ selected_slot->slice_index = KEY_SLOT_CACHE_SLICE_INDEX;
+#endif /* MBEDTLS_PSA_KEY_STORE_DYNAMIC */
+
+#if !defined(MBEDTLS_PSA_KEY_STORE_DYNAMIC)
+ if (volatile_key_id != NULL) {
+ /* Refresh slot_idx, for when the slot is not the original
+ * selected_slot but rather unused_persistent_key_slot. */
+ slot_idx = selected_slot - global_data.key_slots;
+ *volatile_key_id = PSA_KEY_ID_VOLATILE_MIN + slot_idx;
+ }
+#endif
+ *p_slot = selected_slot;
+
+ return PSA_SUCCESS;
+ }
+ status = PSA_ERROR_INSUFFICIENT_MEMORY;
+
+error:
+ *p_slot = NULL;
+
+ return status;
+}
+
+#if defined(MBEDTLS_PSA_CRYPTO_STORAGE_C)
+static psa_status_t psa_load_persistent_key_into_slot(psa_key_slot_t *slot)
+{
+ psa_status_t status = PSA_SUCCESS;
+ uint8_t *key_data = NULL;
+ size_t key_data_length = 0;
+
+ status = psa_load_persistent_key(&slot->attr,
+ &key_data, &key_data_length);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+#if defined(MBEDTLS_PSA_CRYPTO_SE_C)
+ /* Special handling is required for loading keys associated with a
+ * dynamically registered SE interface. */
+ const psa_drv_se_t *drv;
+ psa_drv_se_context_t *drv_context;
+ if (psa_get_se_driver(slot->attr.lifetime, &drv, &drv_context)) {
+ psa_se_key_data_storage_t *data;
+
+ if (key_data_length != sizeof(*data)) {
+ status = PSA_ERROR_DATA_INVALID;
+ goto exit;
+ }
+ data = (psa_se_key_data_storage_t *) key_data;
+ status = psa_copy_key_material_into_slot(
+ slot, data->slot_number, sizeof(data->slot_number));
+ goto exit;
+ }
+#endif /* MBEDTLS_PSA_CRYPTO_SE_C */
+
+ status = psa_copy_key_material_into_slot(slot, key_data, key_data_length);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+exit:
+ psa_free_persistent_key_data(key_data, key_data_length);
+ return status;
+}
+#endif /* MBEDTLS_PSA_CRYPTO_STORAGE_C */
+
+#if defined(MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS)
+
+static psa_status_t psa_load_builtin_key_into_slot(psa_key_slot_t *slot)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
+ psa_key_lifetime_t lifetime = PSA_KEY_LIFETIME_VOLATILE;
+ psa_drv_slot_number_t slot_number = 0;
+ size_t key_buffer_size = 0;
+ size_t key_buffer_length = 0;
+
+ if (!psa_key_id_is_builtin(
+ MBEDTLS_SVC_KEY_ID_GET_KEY_ID(slot->attr.id))) {
+ return PSA_ERROR_DOES_NOT_EXIST;
+ }
+
+ /* Check the platform function to see whether this key actually exists */
+ status = mbedtls_psa_platform_get_builtin_key(
+ slot->attr.id, &lifetime, &slot_number);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ /* Set required key attributes to ensure get_builtin_key can retrieve the
+ * full attributes. */
+ psa_set_key_id(&attributes, slot->attr.id);
+ psa_set_key_lifetime(&attributes, lifetime);
+
+ /* Get the full key attributes from the driver in order to be able to
+ * calculate the required buffer size. */
+ status = psa_driver_wrapper_get_builtin_key(
+ slot_number, &attributes,
+ NULL, 0, NULL);
+ if (status != PSA_ERROR_BUFFER_TOO_SMALL) {
+ /* Builtin keys cannot be defined by the attributes alone */
+ if (status == PSA_SUCCESS) {
+ status = PSA_ERROR_CORRUPTION_DETECTED;
+ }
+ return status;
+ }
+
+ /* If the key should exist according to the platform, then ask the driver
+ * what its expected size is. */
+ status = psa_driver_wrapper_get_key_buffer_size(&attributes,
+ &key_buffer_size);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ /* Allocate a buffer of the required size and load the builtin key directly
+ * into the (now properly sized) slot buffer. */
+ status = psa_allocate_buffer_to_slot(slot, key_buffer_size);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ status = psa_driver_wrapper_get_builtin_key(
+ slot_number, &attributes,
+ slot->key.data, slot->key.bytes, &key_buffer_length);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ /* Copy actual key length and core attributes into the slot on success */
+ slot->key.bytes = key_buffer_length;
+ slot->attr = attributes;
+exit:
+ if (status != PSA_SUCCESS) {
+ psa_remove_key_data_from_memory(slot);
+ }
+ return status;
+}
+#endif /* MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS */
+
+psa_status_t psa_get_and_lock_key_slot(mbedtls_svc_key_id_t key,
+ psa_key_slot_t **p_slot)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+
+ *p_slot = NULL;
+ if (!psa_get_key_slots_initialized()) {
+ return PSA_ERROR_BAD_STATE;
+ }
+
+#if defined(MBEDTLS_THREADING_C)
+ /* We need to set status as success, otherwise CORRUPTION_DETECTED
+ * would be returned if the lock fails. */
+ status = PSA_SUCCESS;
+ /* If the key is persistent and not loaded, we cannot unlock the mutex
+ * between checking if the key is loaded and setting the slot as FULL,
+ * as otherwise another thread may load and then destroy the key
+ * in the meantime. */
+ PSA_THREADING_CHK_RET(mbedtls_mutex_lock(
+ &mbedtls_threading_key_slot_mutex));
+#endif
+ /*
+ * On success, the pointer to the slot is passed directly to the caller
+ * thus no need to unlock the key slot here.
+ */
+ status = psa_get_and_lock_key_slot_in_memory(key, p_slot);
+ if (status != PSA_ERROR_DOES_NOT_EXIST) {
+#if defined(MBEDTLS_THREADING_C)
+ PSA_THREADING_CHK_RET(mbedtls_mutex_unlock(
+ &mbedtls_threading_key_slot_mutex));
+#endif
+ return status;
+ }
+
+ /* Loading keys from storage requires support for such a mechanism */
+#if defined(MBEDTLS_PSA_CRYPTO_STORAGE_C) || \
+ defined(MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS)
+
+ status = psa_reserve_free_key_slot(NULL, p_slot);
+ if (status != PSA_SUCCESS) {
+#if defined(MBEDTLS_THREADING_C)
+ PSA_THREADING_CHK_RET(mbedtls_mutex_unlock(
+ &mbedtls_threading_key_slot_mutex));
+#endif
+ return status;
+ }
+
+ (*p_slot)->attr.id = key;
+ (*p_slot)->attr.lifetime = PSA_KEY_LIFETIME_PERSISTENT;
+
+ status = PSA_ERROR_DOES_NOT_EXIST;
+#if defined(MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS)
+ /* Load keys in the 'builtin' range through their own interface */
+ status = psa_load_builtin_key_into_slot(*p_slot);
+#endif /* MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS */
+
+#if defined(MBEDTLS_PSA_CRYPTO_STORAGE_C)
+ if (status == PSA_ERROR_DOES_NOT_EXIST) {
+ status = psa_load_persistent_key_into_slot(*p_slot);
+ }
+#endif /* defined(MBEDTLS_PSA_CRYPTO_STORAGE_C) */
+
+ if (status != PSA_SUCCESS) {
+ psa_wipe_key_slot(*p_slot);
+
+ /* If the key does not exist, we need to return
+ * PSA_ERROR_INVALID_HANDLE. */
+ if (status == PSA_ERROR_DOES_NOT_EXIST) {
+ status = PSA_ERROR_INVALID_HANDLE;
+ }
+ } else {
+ /* Add implicit usage flags. */
+ psa_extend_key_usage_flags(&(*p_slot)->attr.policy.usage);
+
+ psa_key_slot_state_transition((*p_slot), PSA_SLOT_FILLING,
+ PSA_SLOT_FULL);
+ status = psa_register_read(*p_slot);
+ }
+
+#else /* MBEDTLS_PSA_CRYPTO_STORAGE_C || MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS */
+ status = PSA_ERROR_INVALID_HANDLE;
+#endif /* MBEDTLS_PSA_CRYPTO_STORAGE_C || MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS */
+
+ if (status != PSA_SUCCESS) {
+ *p_slot = NULL;
+ }
+#if defined(MBEDTLS_THREADING_C)
+ PSA_THREADING_CHK_RET(mbedtls_mutex_unlock(
+ &mbedtls_threading_key_slot_mutex));
+#endif
+ return status;
+}
+
+psa_status_t psa_unregister_read(psa_key_slot_t *slot)
+{
+ if (slot == NULL) {
+ return PSA_SUCCESS;
+ }
+ if ((slot->state != PSA_SLOT_FULL) &&
+ (slot->state != PSA_SLOT_PENDING_DELETION)) {
+ return PSA_ERROR_CORRUPTION_DETECTED;
+ }
+
+ /* If we are the last reader and the slot is marked for deletion,
+ * we must wipe the slot here. */
+ if ((slot->state == PSA_SLOT_PENDING_DELETION) &&
+ (slot->var.occupied.registered_readers == 1)) {
+ return psa_wipe_key_slot(slot);
+ }
+
+ if (psa_key_slot_has_readers(slot)) {
+ slot->var.occupied.registered_readers--;
+ return PSA_SUCCESS;
+ }
+
+ /*
+ * As the return error code may not be handled in case of multiple errors,
+ * do our best to report if there are no registered readers. Assert with
+ * MBEDTLS_TEST_HOOK_TEST_ASSERT that there are registered readers:
+ * if the MBEDTLS_TEST_HOOKS configuration option is enabled and
+ * the function is called as part of the execution of a test suite, the
+ * execution of the test suite is stopped in error if the assertion fails.
+ */
+ MBEDTLS_TEST_HOOK_TEST_ASSERT(psa_key_slot_has_readers(slot));
+ return PSA_ERROR_CORRUPTION_DETECTED;
+}
+
+psa_status_t psa_unregister_read_under_mutex(psa_key_slot_t *slot)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+#if defined(MBEDTLS_THREADING_C)
+ /* We need to set status as success, otherwise CORRUPTION_DETECTED
+ * would be returned if the lock fails. */
+ status = PSA_SUCCESS;
+ PSA_THREADING_CHK_RET(mbedtls_mutex_lock(
+ &mbedtls_threading_key_slot_mutex));
+#endif
+ status = psa_unregister_read(slot);
+#if defined(MBEDTLS_THREADING_C)
+ PSA_THREADING_CHK_RET(mbedtls_mutex_unlock(
+ &mbedtls_threading_key_slot_mutex));
+#endif
+ return status;
+}
+
+psa_status_t psa_validate_key_location(psa_key_lifetime_t lifetime,
+ psa_se_drv_table_entry_t **p_drv)
+{
+ if (psa_key_lifetime_is_external(lifetime)) {
+#if defined(MBEDTLS_PSA_CRYPTO_SE_C)
+ /* Check whether a driver is registered against this lifetime */
+ psa_se_drv_table_entry_t *driver = psa_get_se_driver_entry(lifetime);
+ if (driver != NULL) {
+ if (p_drv != NULL) {
+ *p_drv = driver;
+ }
+ return PSA_SUCCESS;
+ }
+#else /* MBEDTLS_PSA_CRYPTO_SE_C */
+ (void) p_drv;
+#endif /* MBEDTLS_PSA_CRYPTO_SE_C */
+
+ /* Key location for external keys gets checked by the wrapper */
+ return PSA_SUCCESS;
+ } else {
+ /* Local/internal keys are always valid */
+ return PSA_SUCCESS;
+ }
+}
+
+psa_status_t psa_validate_key_persistence(psa_key_lifetime_t lifetime)
+{
+ if (PSA_KEY_LIFETIME_IS_VOLATILE(lifetime)) {
+ /* Volatile keys are always supported */
+ return PSA_SUCCESS;
+ } else {
+ /* Persistent keys require storage support */
+#if defined(MBEDTLS_PSA_CRYPTO_STORAGE_C)
+ if (PSA_KEY_LIFETIME_IS_READ_ONLY(lifetime)) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ } else {
+ return PSA_SUCCESS;
+ }
+#else /* MBEDTLS_PSA_CRYPTO_STORAGE_C */
+ return PSA_ERROR_NOT_SUPPORTED;
+#endif /* !MBEDTLS_PSA_CRYPTO_STORAGE_C */
+ }
+}
+
+psa_status_t psa_open_key(mbedtls_svc_key_id_t key, psa_key_handle_t *handle)
+{
+#if defined(MBEDTLS_PSA_CRYPTO_STORAGE_C) || \
+ defined(MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS)
+ psa_status_t status;
+ psa_key_slot_t *slot;
+
+ status = psa_get_and_lock_key_slot(key, &slot);
+ if (status != PSA_SUCCESS) {
+ *handle = PSA_KEY_HANDLE_INIT;
+ if (status == PSA_ERROR_INVALID_HANDLE) {
+ status = PSA_ERROR_DOES_NOT_EXIST;
+ }
+
+ return status;
+ }
+
+ *handle = key;
+
+ return psa_unregister_read_under_mutex(slot);
+
+#else /* MBEDTLS_PSA_CRYPTO_STORAGE_C || MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS */
+ (void) key;
+ *handle = PSA_KEY_HANDLE_INIT;
+ return PSA_ERROR_NOT_SUPPORTED;
+#endif /* MBEDTLS_PSA_CRYPTO_STORAGE_C || MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS */
+}
+
+psa_status_t psa_close_key(psa_key_handle_t handle)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_key_slot_t *slot;
+
+ if (psa_key_handle_is_null(handle)) {
+ return PSA_SUCCESS;
+ }
+
+#if defined(MBEDTLS_THREADING_C)
+ /* We need to set status as success, otherwise CORRUPTION_DETECTED
+ * would be returned if the lock fails. */
+ status = PSA_SUCCESS;
+ PSA_THREADING_CHK_RET(mbedtls_mutex_lock(
+ &mbedtls_threading_key_slot_mutex));
+#endif
+ status = psa_get_and_lock_key_slot_in_memory(handle, &slot);
+ if (status != PSA_SUCCESS) {
+ if (status == PSA_ERROR_DOES_NOT_EXIST) {
+ status = PSA_ERROR_INVALID_HANDLE;
+ }
+#if defined(MBEDTLS_THREADING_C)
+ PSA_THREADING_CHK_RET(mbedtls_mutex_unlock(
+ &mbedtls_threading_key_slot_mutex));
+#endif
+ return status;
+ }
+
+ if (slot->var.occupied.registered_readers == 1) {
+ status = psa_wipe_key_slot(slot);
+ } else {
+ status = psa_unregister_read(slot);
+ }
+#if defined(MBEDTLS_THREADING_C)
+ PSA_THREADING_CHK_RET(mbedtls_mutex_unlock(
+ &mbedtls_threading_key_slot_mutex));
+#endif
+
+ return status;
+}
+
+psa_status_t psa_purge_key(mbedtls_svc_key_id_t key)
+{
+ psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
+ psa_key_slot_t *slot;
+
+#if defined(MBEDTLS_THREADING_C)
+ /* We need to set status as success, otherwise CORRUPTION_DETECTED
+ * would be returned if the lock fails. */
+ status = PSA_SUCCESS;
+ PSA_THREADING_CHK_RET(mbedtls_mutex_lock(
+ &mbedtls_threading_key_slot_mutex));
+#endif
+ status = psa_get_and_lock_key_slot_in_memory(key, &slot);
+ if (status != PSA_SUCCESS) {
+#if defined(MBEDTLS_THREADING_C)
+ PSA_THREADING_CHK_RET(mbedtls_mutex_unlock(
+ &mbedtls_threading_key_slot_mutex));
+#endif
+ return status;
+ }
+
+ if ((!PSA_KEY_LIFETIME_IS_VOLATILE(slot->attr.lifetime)) &&
+ (slot->var.occupied.registered_readers == 1)) {
+ status = psa_wipe_key_slot(slot);
+ } else {
+ status = psa_unregister_read(slot);
+ }
+#if defined(MBEDTLS_THREADING_C)
+ PSA_THREADING_CHK_RET(mbedtls_mutex_unlock(
+ &mbedtls_threading_key_slot_mutex));
+#endif
+
+ return status;
+}
+
+void mbedtls_psa_get_stats(mbedtls_psa_stats_t *stats)
+{
+ memset(stats, 0, sizeof(*stats));
+
+ for (size_t slice_idx = 0; slice_idx < KEY_SLICE_COUNT; slice_idx++) {
+#if defined(MBEDTLS_PSA_KEY_STORE_DYNAMIC)
+ if (global_data.key_slices[slice_idx] == NULL) {
+ continue;
+ }
+#endif /* MBEDTLS_PSA_KEY_STORE_DYNAMIC */
+ for (size_t slot_idx = 0; slot_idx < key_slice_length(slice_idx); slot_idx++) {
+ const psa_key_slot_t *slot = get_key_slot(slice_idx, slot_idx);
+ if (slot->state == PSA_SLOT_EMPTY) {
+ ++stats->empty_slots;
+ continue;
+ }
+ if (psa_key_slot_has_readers(slot)) {
+ ++stats->locked_slots;
+ }
+ if (PSA_KEY_LIFETIME_IS_VOLATILE(slot->attr.lifetime)) {
+ ++stats->volatile_slots;
+ } else {
+ psa_key_id_t id = MBEDTLS_SVC_KEY_ID_GET_KEY_ID(slot->attr.id);
+ ++stats->persistent_slots;
+ if (id > stats->max_open_internal_key_id) {
+ stats->max_open_internal_key_id = id;
+ }
+ }
+ if (PSA_KEY_LIFETIME_GET_LOCATION(slot->attr.lifetime) !=
+ PSA_KEY_LOCATION_LOCAL_STORAGE) {
+ psa_key_id_t id = MBEDTLS_SVC_KEY_ID_GET_KEY_ID(slot->attr.id);
+ ++stats->external_slots;
+ if (id > stats->max_open_external_key_id) {
+ stats->max_open_external_key_id = id;
+ }
+ }
+ }
+ }
+}
+
+#endif /* MBEDTLS_PSA_CRYPTO_C */
diff --git a/thirdparty/mbedtls/library/psa_crypto_storage.c b/thirdparty/mbedtls/library/psa_crypto_storage.c
new file mode 100644
index 0000000000..7d1317b45a
--- /dev/null
+++ b/thirdparty/mbedtls/library/psa_crypto_storage.c
@@ -0,0 +1,481 @@
+/*
+ * PSA persistent key storage
+ */
+/*
+ * Copyright The Mbed TLS Contributors
+ * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#include "common.h"
+
+#if defined(MBEDTLS_PSA_CRYPTO_STORAGE_C)
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "psa/crypto.h"
+#include "psa_crypto_storage.h"
+#include "mbedtls/platform_util.h"
+
+#if defined(MBEDTLS_PSA_ITS_FILE_C)
+#include "psa_crypto_its.h"
+#else /* Native ITS implementation */
+#include "psa/error.h"
+#include "psa/internal_trusted_storage.h"
+#endif
+
+#include "mbedtls/platform.h"
+
+
+
+/****************************************************************/
+/* Key storage */
+/****************************************************************/
+
+/* Determine a file name (ITS file identifier) for the given key identifier.
+ * The file name must be distinct from any file that is used for a purpose
+ * other than storing a key. Currently, the only such file is the random seed
+ * file whose name is PSA_CRYPTO_ITS_RANDOM_SEED_UID and whose value is
+ * 0xFFFFFF52. */
+static psa_storage_uid_t psa_its_identifier_of_slot(mbedtls_svc_key_id_t key)
+{
+#if defined(MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER)
+ /* Encode the owner in the upper 32 bits. This means that if
+ * owner values are nonzero (as they are on a PSA platform),
+ * no key file will ever have a value less than 0x100000000, so
+ * the whole range 0..0xffffffff is available for non-key files. */
+ uint32_t unsigned_owner_id = MBEDTLS_SVC_KEY_ID_GET_OWNER_ID(key);
+ return ((uint64_t) unsigned_owner_id << 32) |
+ MBEDTLS_SVC_KEY_ID_GET_KEY_ID(key);
+#else
+ /* Use the key id directly as a file name.
+ * psa_is_key_id_valid() in psa_crypto_slot_management.c
+ * is responsible for ensuring that key identifiers do not have a
+ * value that is reserved for non-key files. */
+ return key;
+#endif
+}
+
+/**
+ * \brief Load persistent data for the given key slot number.
+ *
+ * This function reads data from a storage backend and returns the data in a
+ * buffer.
+ *
+ * \param key Persistent identifier of the key to be loaded. This
+ * should be an occupied storage location.
+ * \param[out] data Buffer where the data is to be written.
+ * \param data_size Size of the \c data buffer in bytes.
+ *
+ * \retval #PSA_SUCCESS \emptydescription
+ * \retval #PSA_ERROR_DATA_INVALID \emptydescription
+ * \retval #PSA_ERROR_DATA_CORRUPT \emptydescription
+ * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription
+ * \retval #PSA_ERROR_DOES_NOT_EXIST \emptydescription
+ */
+static psa_status_t psa_crypto_storage_load(
+ const mbedtls_svc_key_id_t key, uint8_t *data, size_t data_size)
+{
+ psa_status_t status;
+ psa_storage_uid_t data_identifier = psa_its_identifier_of_slot(key);
+ struct psa_storage_info_t data_identifier_info;
+ size_t data_length = 0;
+
+ status = psa_its_get_info(data_identifier, &data_identifier_info);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ status = psa_its_get(data_identifier, 0, (uint32_t) data_size, data, &data_length);
+ if (data_size != data_length) {
+ return PSA_ERROR_DATA_INVALID;
+ }
+
+ return status;
+}
+
+int psa_is_key_present_in_storage(const mbedtls_svc_key_id_t key)
+{
+ psa_status_t ret;
+ psa_storage_uid_t data_identifier = psa_its_identifier_of_slot(key);
+ struct psa_storage_info_t data_identifier_info;
+
+ ret = psa_its_get_info(data_identifier, &data_identifier_info);
+
+ if (ret == PSA_ERROR_DOES_NOT_EXIST) {
+ return 0;
+ }
+ return 1;
+}
+
+/**
+ * \brief Store persistent data for the given key slot number.
+ *
+ * This function stores the given data buffer to a persistent storage.
+ *
+ * \param key Persistent identifier of the key to be stored. This
+ * should be an unoccupied storage location.
+ * \param[in] data Buffer containing the data to be stored.
+ * \param data_length The number of bytes
+ * that make up the data.
+ *
+ * \retval #PSA_SUCCESS \emptydescription
+ * \retval #PSA_ERROR_INSUFFICIENT_STORAGE \emptydescription
+ * \retval #PSA_ERROR_ALREADY_EXISTS \emptydescription
+ * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription
+ * \retval #PSA_ERROR_DATA_INVALID \emptydescription
+ */
+static psa_status_t psa_crypto_storage_store(const mbedtls_svc_key_id_t key,
+ const uint8_t *data,
+ size_t data_length)
+{
+ psa_status_t status;
+ psa_storage_uid_t data_identifier = psa_its_identifier_of_slot(key);
+ struct psa_storage_info_t data_identifier_info;
+
+ if (psa_is_key_present_in_storage(key) == 1) {
+ return PSA_ERROR_ALREADY_EXISTS;
+ }
+
+ status = psa_its_set(data_identifier, (uint32_t) data_length, data, 0);
+ if (status != PSA_SUCCESS) {
+ return PSA_ERROR_DATA_INVALID;
+ }
+
+ status = psa_its_get_info(data_identifier, &data_identifier_info);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ if (data_identifier_info.size != data_length) {
+ status = PSA_ERROR_DATA_INVALID;
+ goto exit;
+ }
+
+exit:
+ if (status != PSA_SUCCESS) {
+ /* Remove the file in case we managed to create it but something
+ * went wrong. It's ok if the file doesn't exist. If the file exists
+ * but the removal fails, we're already reporting an error so there's
+ * nothing else we can do. */
+ (void) psa_its_remove(data_identifier);
+ }
+ return status;
+}
+
+psa_status_t psa_destroy_persistent_key(const mbedtls_svc_key_id_t key)
+{
+ psa_status_t ret;
+ psa_storage_uid_t data_identifier = psa_its_identifier_of_slot(key);
+ struct psa_storage_info_t data_identifier_info;
+
+ ret = psa_its_get_info(data_identifier, &data_identifier_info);
+ if (ret == PSA_ERROR_DOES_NOT_EXIST) {
+ return PSA_SUCCESS;
+ }
+
+ if (psa_its_remove(data_identifier) != PSA_SUCCESS) {
+ return PSA_ERROR_DATA_INVALID;
+ }
+
+ ret = psa_its_get_info(data_identifier, &data_identifier_info);
+ if (ret != PSA_ERROR_DOES_NOT_EXIST) {
+ return PSA_ERROR_DATA_INVALID;
+ }
+
+ return PSA_SUCCESS;
+}
+
+/**
+ * \brief Get data length for given key slot number.
+ *
+ * \param key Persistent identifier whose stored data length
+ * is to be obtained.
+ * \param[out] data_length The number of bytes that make up the data.
+ *
+ * \retval #PSA_SUCCESS \emptydescription
+ * \retval #PSA_ERROR_STORAGE_FAILURE \emptydescription
+ * \retval #PSA_ERROR_DOES_NOT_EXIST \emptydescription
+ * \retval #PSA_ERROR_DATA_CORRUPT \emptydescription
+ */
+static psa_status_t psa_crypto_storage_get_data_length(
+ const mbedtls_svc_key_id_t key,
+ size_t *data_length)
+{
+ psa_status_t status;
+ psa_storage_uid_t data_identifier = psa_its_identifier_of_slot(key);
+ struct psa_storage_info_t data_identifier_info;
+
+ status = psa_its_get_info(data_identifier, &data_identifier_info);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ *data_length = (size_t) data_identifier_info.size;
+
+ return PSA_SUCCESS;
+}
+
+/**
+ * Persistent key storage magic header.
+ */
+#define PSA_KEY_STORAGE_MAGIC_HEADER "PSA\0KEY"
+#define PSA_KEY_STORAGE_MAGIC_HEADER_LENGTH (sizeof(PSA_KEY_STORAGE_MAGIC_HEADER))
+
+typedef struct {
+ uint8_t magic[PSA_KEY_STORAGE_MAGIC_HEADER_LENGTH];
+ uint8_t version[4];
+ uint8_t lifetime[sizeof(psa_key_lifetime_t)];
+ uint8_t type[2];
+ uint8_t bits[2];
+ uint8_t policy[sizeof(psa_key_policy_t)];
+ uint8_t data_len[4];
+ uint8_t key_data[];
+} psa_persistent_key_storage_format;
+
+void psa_format_key_data_for_storage(const uint8_t *data,
+ const size_t data_length,
+ const psa_key_attributes_t *attr,
+ uint8_t *storage_data)
+{
+ psa_persistent_key_storage_format *storage_format =
+ (psa_persistent_key_storage_format *) storage_data;
+
+ memcpy(storage_format->magic, PSA_KEY_STORAGE_MAGIC_HEADER,
+ PSA_KEY_STORAGE_MAGIC_HEADER_LENGTH);
+ MBEDTLS_PUT_UINT32_LE(0, storage_format->version, 0);
+ MBEDTLS_PUT_UINT32_LE(attr->lifetime, storage_format->lifetime, 0);
+ MBEDTLS_PUT_UINT16_LE((uint16_t) attr->type, storage_format->type, 0);
+ MBEDTLS_PUT_UINT16_LE((uint16_t) attr->bits, storage_format->bits, 0);
+ MBEDTLS_PUT_UINT32_LE(attr->policy.usage, storage_format->policy, 0);
+ MBEDTLS_PUT_UINT32_LE(attr->policy.alg, storage_format->policy, sizeof(uint32_t));
+ MBEDTLS_PUT_UINT32_LE(attr->policy.alg2, storage_format->policy, 2 * sizeof(uint32_t));
+ MBEDTLS_PUT_UINT32_LE(data_length, storage_format->data_len, 0);
+ memcpy(storage_format->key_data, data, data_length);
+}
+
+static psa_status_t check_magic_header(const uint8_t *data)
+{
+ if (memcmp(data, PSA_KEY_STORAGE_MAGIC_HEADER,
+ PSA_KEY_STORAGE_MAGIC_HEADER_LENGTH) != 0) {
+ return PSA_ERROR_DATA_INVALID;
+ }
+ return PSA_SUCCESS;
+}
+
+psa_status_t psa_parse_key_data_from_storage(const uint8_t *storage_data,
+ size_t storage_data_length,
+ uint8_t **key_data,
+ size_t *key_data_length,
+ psa_key_attributes_t *attr)
+{
+ psa_status_t status;
+ const psa_persistent_key_storage_format *storage_format =
+ (const psa_persistent_key_storage_format *) storage_data;
+ uint32_t version;
+
+ if (storage_data_length < sizeof(*storage_format)) {
+ return PSA_ERROR_DATA_INVALID;
+ }
+
+ status = check_magic_header(storage_data);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ version = MBEDTLS_GET_UINT32_LE(storage_format->version, 0);
+ if (version != 0) {
+ return PSA_ERROR_DATA_INVALID;
+ }
+
+ *key_data_length = MBEDTLS_GET_UINT32_LE(storage_format->data_len, 0);
+ if (*key_data_length > (storage_data_length - sizeof(*storage_format)) ||
+ *key_data_length > PSA_CRYPTO_MAX_STORAGE_SIZE) {
+ return PSA_ERROR_DATA_INVALID;
+ }
+
+ if (*key_data_length == 0) {
+ *key_data = NULL;
+ } else {
+ *key_data = mbedtls_calloc(1, *key_data_length);
+ if (*key_data == NULL) {
+ return PSA_ERROR_INSUFFICIENT_MEMORY;
+ }
+ memcpy(*key_data, storage_format->key_data, *key_data_length);
+ }
+
+ attr->lifetime = MBEDTLS_GET_UINT32_LE(storage_format->lifetime, 0);
+ attr->type = MBEDTLS_GET_UINT16_LE(storage_format->type, 0);
+ attr->bits = MBEDTLS_GET_UINT16_LE(storage_format->bits, 0);
+ attr->policy.usage = MBEDTLS_GET_UINT32_LE(storage_format->policy, 0);
+ attr->policy.alg = MBEDTLS_GET_UINT32_LE(storage_format->policy, sizeof(uint32_t));
+ attr->policy.alg2 = MBEDTLS_GET_UINT32_LE(storage_format->policy, 2 * sizeof(uint32_t));
+
+ return PSA_SUCCESS;
+}
+
+psa_status_t psa_save_persistent_key(const psa_key_attributes_t *attr,
+ const uint8_t *data,
+ const size_t data_length)
+{
+ size_t storage_data_length;
+ uint8_t *storage_data;
+ psa_status_t status;
+
+ /* All keys saved to persistent storage always have a key context */
+ if (data == NULL || data_length == 0) {
+ return PSA_ERROR_INVALID_ARGUMENT;
+ }
+
+ if (data_length > PSA_CRYPTO_MAX_STORAGE_SIZE) {
+ return PSA_ERROR_INSUFFICIENT_STORAGE;
+ }
+ storage_data_length = data_length + sizeof(psa_persistent_key_storage_format);
+
+ storage_data = mbedtls_calloc(1, storage_data_length);
+ if (storage_data == NULL) {
+ return PSA_ERROR_INSUFFICIENT_MEMORY;
+ }
+
+ psa_format_key_data_for_storage(data, data_length, attr, storage_data);
+
+ status = psa_crypto_storage_store(attr->id,
+ storage_data, storage_data_length);
+
+ mbedtls_zeroize_and_free(storage_data, storage_data_length);
+
+ return status;
+}
+
+void psa_free_persistent_key_data(uint8_t *key_data, size_t key_data_length)
+{
+ mbedtls_zeroize_and_free(key_data, key_data_length);
+}
+
+psa_status_t psa_load_persistent_key(psa_key_attributes_t *attr,
+ uint8_t **data,
+ size_t *data_length)
+{
+ psa_status_t status = PSA_SUCCESS;
+ uint8_t *loaded_data;
+ size_t storage_data_length = 0;
+ mbedtls_svc_key_id_t key = attr->id;
+
+ status = psa_crypto_storage_get_data_length(key, &storage_data_length);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+
+ loaded_data = mbedtls_calloc(1, storage_data_length);
+
+ if (loaded_data == NULL) {
+ return PSA_ERROR_INSUFFICIENT_MEMORY;
+ }
+
+ status = psa_crypto_storage_load(key, loaded_data, storage_data_length);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+
+ status = psa_parse_key_data_from_storage(loaded_data, storage_data_length,
+ data, data_length, attr);
+
+ /* All keys saved to persistent storage always have a key context */
+ if (status == PSA_SUCCESS &&
+ (*data == NULL || *data_length == 0)) {
+ status = PSA_ERROR_STORAGE_FAILURE;
+ }
+
+exit:
+ mbedtls_zeroize_and_free(loaded_data, storage_data_length);
+ return status;
+}
+
+
+
+/****************************************************************/
+/* Transactions */
+/****************************************************************/
+
+#if defined(PSA_CRYPTO_STORAGE_HAS_TRANSACTIONS)
+
+psa_crypto_transaction_t psa_crypto_transaction;
+
+psa_status_t psa_crypto_save_transaction(void)
+{
+ struct psa_storage_info_t p_info;
+ psa_status_t status;
+ status = psa_its_get_info(PSA_CRYPTO_ITS_TRANSACTION_UID, &p_info);
+ if (status == PSA_SUCCESS) {
+ /* This shouldn't happen: we're trying to start a transaction while
+ * there is still a transaction that hasn't been replayed. */
+ return PSA_ERROR_CORRUPTION_DETECTED;
+ } else if (status != PSA_ERROR_DOES_NOT_EXIST) {
+ return status;
+ }
+ return psa_its_set(PSA_CRYPTO_ITS_TRANSACTION_UID,
+ sizeof(psa_crypto_transaction),
+ &psa_crypto_transaction,
+ 0);
+}
+
+psa_status_t psa_crypto_load_transaction(void)
+{
+ psa_status_t status;
+ size_t length;
+ status = psa_its_get(PSA_CRYPTO_ITS_TRANSACTION_UID, 0,
+ sizeof(psa_crypto_transaction),
+ &psa_crypto_transaction, &length);
+ if (status != PSA_SUCCESS) {
+ return status;
+ }
+ if (length != sizeof(psa_crypto_transaction)) {
+ return PSA_ERROR_DATA_INVALID;
+ }
+ return PSA_SUCCESS;
+}
+
+psa_status_t psa_crypto_stop_transaction(void)
+{
+ psa_status_t status = psa_its_remove(PSA_CRYPTO_ITS_TRANSACTION_UID);
+ /* Whether or not updating the storage succeeded, the transaction is
+ * finished now. It's too late to go back, so zero out the in-memory
+ * data. */
+ memset(&psa_crypto_transaction, 0, sizeof(psa_crypto_transaction));
+ return status;
+}
+
+#endif /* PSA_CRYPTO_STORAGE_HAS_TRANSACTIONS */
+
+
+
+/****************************************************************/
+/* Random generator state */
+/****************************************************************/
+
+#if defined(MBEDTLS_PSA_INJECT_ENTROPY)
+psa_status_t mbedtls_psa_storage_inject_entropy(const unsigned char *seed,
+ size_t seed_size)
+{
+ psa_status_t status;
+ struct psa_storage_info_t p_info;
+
+ status = psa_its_get_info(PSA_CRYPTO_ITS_RANDOM_SEED_UID, &p_info);
+
+ if (PSA_ERROR_DOES_NOT_EXIST == status) { /* No seed exists */
+ status = psa_its_set(PSA_CRYPTO_ITS_RANDOM_SEED_UID, seed_size, seed, 0);
+ } else if (PSA_SUCCESS == status) {
+ /* You should not be here. Seed needs to be injected only once */
+ status = PSA_ERROR_NOT_PERMITTED;
+ }
+ return status;
+}
+#endif /* MBEDTLS_PSA_INJECT_ENTROPY */
+
+
+
+/****************************************************************/
+/* The end */
+/****************************************************************/
+
+#endif /* MBEDTLS_PSA_CRYPTO_STORAGE_C */
diff --git a/thirdparty/mbedtls/library/psa_its_file.c b/thirdparty/mbedtls/library/psa_its_file.c
new file mode 100644
index 0000000000..9567137483
--- /dev/null
+++ b/thirdparty/mbedtls/library/psa_its_file.c
@@ -0,0 +1,254 @@
+/*
+ * PSA ITS simulator over stdio files.
+ */
+/*
+ * Copyright The Mbed TLS Contributors
+ * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#include "common.h"
+
+#if defined(MBEDTLS_PSA_ITS_FILE_C)
+
+#include "mbedtls/platform.h"
+
+#if defined(_WIN32)
+#include <windows.h>
+#endif
+
+#include "psa_crypto_its.h"
+
+#include <limits.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#if !defined(PSA_ITS_STORAGE_PREFIX)
+#define PSA_ITS_STORAGE_PREFIX ""
+#endif
+
+#define PSA_ITS_STORAGE_FILENAME_PATTERN "%08x%08x"
+#define PSA_ITS_STORAGE_SUFFIX ".psa_its"
+#define PSA_ITS_STORAGE_FILENAME_LENGTH \
+ (sizeof(PSA_ITS_STORAGE_PREFIX) - 1 + /*prefix without terminating 0*/ \
+ 16 + /*UID (64-bit number in hex)*/ \
+ sizeof(PSA_ITS_STORAGE_SUFFIX) - 1 + /*suffix without terminating 0*/ \
+ 1 /*terminating null byte*/)
+#define PSA_ITS_STORAGE_TEMP \
+ PSA_ITS_STORAGE_PREFIX "tempfile" PSA_ITS_STORAGE_SUFFIX
+
+/* The maximum value of psa_storage_info_t.size */
+#define PSA_ITS_MAX_SIZE 0xffffffff
+
+#define PSA_ITS_MAGIC_STRING "PSA\0ITS\0"
+#define PSA_ITS_MAGIC_LENGTH 8
+
+/* As rename fails on Windows if the new filepath already exists,
+ * use MoveFileExA with the MOVEFILE_REPLACE_EXISTING flag instead.
+ * Returns 0 on success, nonzero on failure. */
+#if defined(_WIN32)
+#define rename_replace_existing(oldpath, newpath) \
+ (!MoveFileExA(oldpath, newpath, MOVEFILE_REPLACE_EXISTING))
+#else
+#define rename_replace_existing(oldpath, newpath) rename(oldpath, newpath)
+#endif
+
+typedef struct {
+ uint8_t magic[PSA_ITS_MAGIC_LENGTH];
+ uint8_t size[sizeof(uint32_t)];
+ uint8_t flags[sizeof(psa_storage_create_flags_t)];
+} psa_its_file_header_t;
+
+static void psa_its_fill_filename(psa_storage_uid_t uid, char *filename)
+{
+ /* Break up the UID into two 32-bit pieces so as not to rely on
+ * long long support in snprintf. */
+ mbedtls_snprintf(filename, PSA_ITS_STORAGE_FILENAME_LENGTH,
+ "%s" PSA_ITS_STORAGE_FILENAME_PATTERN "%s",
+ PSA_ITS_STORAGE_PREFIX,
+ (unsigned) (uid >> 32),
+ (unsigned) (uid & 0xffffffff),
+ PSA_ITS_STORAGE_SUFFIX);
+}
+
+static psa_status_t psa_its_read_file(psa_storage_uid_t uid,
+ struct psa_storage_info_t *p_info,
+ FILE **p_stream)
+{
+ char filename[PSA_ITS_STORAGE_FILENAME_LENGTH];
+ psa_its_file_header_t header;
+ size_t n;
+
+ *p_stream = NULL;
+ psa_its_fill_filename(uid, filename);
+ *p_stream = fopen(filename, "rb");
+ if (*p_stream == NULL) {
+ return PSA_ERROR_DOES_NOT_EXIST;
+ }
+
+ /* Ensure no stdio buffering of secrets, as such buffers cannot be wiped. */
+ mbedtls_setbuf(*p_stream, NULL);
+
+ n = fread(&header, 1, sizeof(header), *p_stream);
+ if (n != sizeof(header)) {
+ return PSA_ERROR_DATA_CORRUPT;
+ }
+ if (memcmp(header.magic, PSA_ITS_MAGIC_STRING,
+ PSA_ITS_MAGIC_LENGTH) != 0) {
+ return PSA_ERROR_DATA_CORRUPT;
+ }
+
+ p_info->size = MBEDTLS_GET_UINT32_LE(header.size, 0);
+ p_info->flags = MBEDTLS_GET_UINT32_LE(header.flags, 0);
+
+ return PSA_SUCCESS;
+}
+
+psa_status_t psa_its_get_info(psa_storage_uid_t uid,
+ struct psa_storage_info_t *p_info)
+{
+ psa_status_t status;
+ FILE *stream = NULL;
+ status = psa_its_read_file(uid, p_info, &stream);
+ if (stream != NULL) {
+ fclose(stream);
+ }
+ return status;
+}
+
+psa_status_t psa_its_get(psa_storage_uid_t uid,
+ uint32_t data_offset,
+ uint32_t data_length,
+ void *p_data,
+ size_t *p_data_length)
+{
+ psa_status_t status;
+ FILE *stream = NULL;
+ size_t n;
+ struct psa_storage_info_t info;
+
+ status = psa_its_read_file(uid, &info, &stream);
+ if (status != PSA_SUCCESS) {
+ goto exit;
+ }
+ status = PSA_ERROR_INVALID_ARGUMENT;
+ if (data_offset + data_length < data_offset) {
+ goto exit;
+ }
+#if SIZE_MAX < 0xffffffff
+ if (data_offset + data_length > SIZE_MAX) {
+ goto exit;
+ }
+#endif
+ if (data_offset + data_length > info.size) {
+ goto exit;
+ }
+
+ status = PSA_ERROR_STORAGE_FAILURE;
+#if LONG_MAX < 0xffffffff
+ while (data_offset > LONG_MAX) {
+ if (fseek(stream, LONG_MAX, SEEK_CUR) != 0) {
+ goto exit;
+ }
+ data_offset -= LONG_MAX;
+ }
+#endif
+ if (fseek(stream, data_offset, SEEK_CUR) != 0) {
+ goto exit;
+ }
+ n = fread(p_data, 1, data_length, stream);
+ if (n != data_length) {
+ goto exit;
+ }
+ status = PSA_SUCCESS;
+ if (p_data_length != NULL) {
+ *p_data_length = n;
+ }
+
+exit:
+ if (stream != NULL) {
+ fclose(stream);
+ }
+ return status;
+}
+
+psa_status_t psa_its_set(psa_storage_uid_t uid,
+ uint32_t data_length,
+ const void *p_data,
+ psa_storage_create_flags_t create_flags)
+{
+ if (uid == 0) {
+ return PSA_ERROR_INVALID_HANDLE;
+ }
+
+ psa_status_t status = PSA_ERROR_STORAGE_FAILURE;
+ char filename[PSA_ITS_STORAGE_FILENAME_LENGTH];
+ FILE *stream = NULL;
+ psa_its_file_header_t header;
+ size_t n;
+
+ memcpy(header.magic, PSA_ITS_MAGIC_STRING, PSA_ITS_MAGIC_LENGTH);
+ MBEDTLS_PUT_UINT32_LE(data_length, header.size, 0);
+ MBEDTLS_PUT_UINT32_LE(create_flags, header.flags, 0);
+
+ psa_its_fill_filename(uid, filename);
+ stream = fopen(PSA_ITS_STORAGE_TEMP, "wb");
+
+ if (stream == NULL) {
+ goto exit;
+ }
+
+ /* Ensure no stdio buffering of secrets, as such buffers cannot be wiped. */
+ mbedtls_setbuf(stream, NULL);
+
+ status = PSA_ERROR_INSUFFICIENT_STORAGE;
+ n = fwrite(&header, 1, sizeof(header), stream);
+ if (n != sizeof(header)) {
+ goto exit;
+ }
+ if (data_length != 0) {
+ n = fwrite(p_data, 1, data_length, stream);
+ if (n != data_length) {
+ goto exit;
+ }
+ }
+ status = PSA_SUCCESS;
+
+exit:
+ if (stream != NULL) {
+ int ret = fclose(stream);
+ if (status == PSA_SUCCESS && ret != 0) {
+ status = PSA_ERROR_INSUFFICIENT_STORAGE;
+ }
+ }
+ if (status == PSA_SUCCESS) {
+ if (rename_replace_existing(PSA_ITS_STORAGE_TEMP, filename) != 0) {
+ status = PSA_ERROR_STORAGE_FAILURE;
+ }
+ }
+ /* The temporary file may still exist, but only in failure cases where
+ * we're already reporting an error. So there's nothing we can do on
+ * failure. If the function succeeded, and in some error cases, the
+ * temporary file doesn't exist and so remove() is expected to fail.
+ * Thus we just ignore the return status of remove(). */
+ (void) remove(PSA_ITS_STORAGE_TEMP);
+ return status;
+}
+
+psa_status_t psa_its_remove(psa_storage_uid_t uid)
+{
+ char filename[PSA_ITS_STORAGE_FILENAME_LENGTH];
+ FILE *stream;
+ psa_its_fill_filename(uid, filename);
+ stream = fopen(filename, "rb");
+ if (stream == NULL) {
+ return PSA_ERROR_DOES_NOT_EXIST;
+ }
+ fclose(stream);
+ if (remove(filename) != 0) {
+ return PSA_ERROR_STORAGE_FAILURE;
+ }
+ return PSA_SUCCESS;
+}
+
+#endif /* MBEDTLS_PSA_ITS_FILE_C */
diff --git a/thirdparty/mbedtls/library/psa_util.c b/thirdparty/mbedtls/library/psa_util.c
new file mode 100644
index 0000000000..679d00ea9b
--- /dev/null
+++ b/thirdparty/mbedtls/library/psa_util.c
@@ -0,0 +1,608 @@
+/*
+ * PSA hashing layer on top of Mbed TLS software crypto
+ */
+/*
+ * Copyright The Mbed TLS Contributors
+ * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+ */
+
+#include "common.h"
+
+/* This is needed for MBEDTLS_ERR_XXX macros */
+#include <mbedtls/error.h>
+
+#if defined(MBEDTLS_ASN1_WRITE_C)
+#include <mbedtls/asn1write.h>
+#include <psa/crypto_sizes.h>
+#endif
+
+#include "psa_util_internal.h"
+
+#if defined(MBEDTLS_PSA_CRYPTO_CLIENT)
+
+#include <psa/crypto.h>
+
+#if defined(MBEDTLS_MD_LIGHT)
+#include <mbedtls/md.h>
+#endif
+#if defined(MBEDTLS_LMS_C)
+#include <mbedtls/lms.h>
+#endif
+#if defined(MBEDTLS_SSL_TLS_C) && \
+ (defined(MBEDTLS_USE_PSA_CRYPTO) || defined(MBEDTLS_SSL_PROTO_TLS1_3))
+#include <mbedtls/ssl.h>
+#endif
+#if defined(PSA_WANT_KEY_TYPE_RSA_PUBLIC_KEY) || \
+ defined(PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC)
+#include <mbedtls/rsa.h>
+#endif
+#if defined(MBEDTLS_USE_PSA_CRYPTO) && \
+ defined(PSA_WANT_KEY_TYPE_ECC_PUBLIC_KEY)
+#include <mbedtls/ecp.h>
+#endif
+#if defined(MBEDTLS_PK_C)
+#include <mbedtls/pk.h>
+#endif
+#if defined(MBEDTLS_BLOCK_CIPHER_SOME_PSA)
+#include <mbedtls/cipher.h>
+#endif
+#include <mbedtls/entropy.h>
+
+/* PSA_SUCCESS is kept at the top of each error table since
+ * it's the most common status when everything functions properly. */
+#if defined(MBEDTLS_MD_LIGHT)
+const mbedtls_error_pair_t psa_to_md_errors[] =
+{
+ { PSA_SUCCESS, 0 },
+ { PSA_ERROR_NOT_SUPPORTED, MBEDTLS_ERR_MD_FEATURE_UNAVAILABLE },
+ { PSA_ERROR_INVALID_ARGUMENT, MBEDTLS_ERR_MD_BAD_INPUT_DATA },
+ { PSA_ERROR_INSUFFICIENT_MEMORY, MBEDTLS_ERR_MD_ALLOC_FAILED }
+};
+#endif
+
+#if defined(MBEDTLS_BLOCK_CIPHER_SOME_PSA)
+const mbedtls_error_pair_t psa_to_cipher_errors[] =
+{
+ { PSA_SUCCESS, 0 },
+ { PSA_ERROR_NOT_SUPPORTED, MBEDTLS_ERR_CIPHER_FEATURE_UNAVAILABLE },
+ { PSA_ERROR_INVALID_ARGUMENT, MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA },
+ { PSA_ERROR_INSUFFICIENT_MEMORY, MBEDTLS_ERR_CIPHER_ALLOC_FAILED }
+};
+#endif
+
+#if defined(MBEDTLS_LMS_C)
+const mbedtls_error_pair_t psa_to_lms_errors[] =
+{
+ { PSA_SUCCESS, 0 },
+ { PSA_ERROR_BUFFER_TOO_SMALL, MBEDTLS_ERR_LMS_BUFFER_TOO_SMALL },
+ { PSA_ERROR_INVALID_ARGUMENT, MBEDTLS_ERR_LMS_BAD_INPUT_DATA }
+};
+#endif
+
+#if defined(MBEDTLS_SSL_TLS_C) && \
+ (defined(MBEDTLS_USE_PSA_CRYPTO) || defined(MBEDTLS_SSL_PROTO_TLS1_3))
+const mbedtls_error_pair_t psa_to_ssl_errors[] =
+{
+ { PSA_SUCCESS, 0 },
+ { PSA_ERROR_INSUFFICIENT_MEMORY, MBEDTLS_ERR_SSL_ALLOC_FAILED },
+ { PSA_ERROR_NOT_SUPPORTED, MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE },
+ { PSA_ERROR_INVALID_SIGNATURE, MBEDTLS_ERR_SSL_INVALID_MAC },
+ { PSA_ERROR_INVALID_ARGUMENT, MBEDTLS_ERR_SSL_BAD_INPUT_DATA },
+ { PSA_ERROR_BAD_STATE, MBEDTLS_ERR_SSL_INTERNAL_ERROR },
+ { PSA_ERROR_BUFFER_TOO_SMALL, MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL }
+};
+#endif
+
+#if defined(PSA_WANT_KEY_TYPE_RSA_PUBLIC_KEY) || \
+ defined(PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC)
+const mbedtls_error_pair_t psa_to_pk_rsa_errors[] =
+{
+ { PSA_SUCCESS, 0 },
+ { PSA_ERROR_NOT_PERMITTED, MBEDTLS_ERR_RSA_BAD_INPUT_DATA },
+ { PSA_ERROR_INVALID_ARGUMENT, MBEDTLS_ERR_RSA_BAD_INPUT_DATA },
+ { PSA_ERROR_INVALID_HANDLE, MBEDTLS_ERR_RSA_BAD_INPUT_DATA },
+ { PSA_ERROR_BUFFER_TOO_SMALL, MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE },
+ { PSA_ERROR_INSUFFICIENT_ENTROPY, MBEDTLS_ERR_RSA_RNG_FAILED },
+ { PSA_ERROR_INVALID_SIGNATURE, MBEDTLS_ERR_RSA_VERIFY_FAILED },
+ { PSA_ERROR_INVALID_PADDING, MBEDTLS_ERR_RSA_INVALID_PADDING }
+};
+#endif
+
+#if defined(MBEDTLS_USE_PSA_CRYPTO) && \
+ defined(PSA_WANT_KEY_TYPE_ECC_PUBLIC_KEY)
+const mbedtls_error_pair_t psa_to_pk_ecdsa_errors[] =
+{
+ { PSA_SUCCESS, 0 },
+ { PSA_ERROR_NOT_PERMITTED, MBEDTLS_ERR_ECP_BAD_INPUT_DATA },
+ { PSA_ERROR_INVALID_ARGUMENT, MBEDTLS_ERR_ECP_BAD_INPUT_DATA },
+ { PSA_ERROR_INVALID_HANDLE, MBEDTLS_ERR_ECP_FEATURE_UNAVAILABLE },
+ { PSA_ERROR_BUFFER_TOO_SMALL, MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL },
+ { PSA_ERROR_INSUFFICIENT_ENTROPY, MBEDTLS_ERR_ECP_RANDOM_FAILED },
+ { PSA_ERROR_INVALID_SIGNATURE, MBEDTLS_ERR_ECP_VERIFY_FAILED }
+};
+#endif
+
+int psa_generic_status_to_mbedtls(psa_status_t status)
+{
+ switch (status) {
+ case PSA_SUCCESS:
+ return 0;
+ case PSA_ERROR_NOT_SUPPORTED:
+ return MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED;
+ case PSA_ERROR_CORRUPTION_DETECTED:
+ return MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+ case PSA_ERROR_COMMUNICATION_FAILURE:
+ case PSA_ERROR_HARDWARE_FAILURE:
+ return MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED;
+ case PSA_ERROR_NOT_PERMITTED:
+ default:
+ return MBEDTLS_ERR_ERROR_GENERIC_ERROR;
+ }
+}
+
+int psa_status_to_mbedtls(psa_status_t status,
+ const mbedtls_error_pair_t *local_translations,
+ size_t local_errors_num,
+ int (*fallback_f)(psa_status_t))
+{
+ for (size_t i = 0; i < local_errors_num; i++) {
+ if (status == local_translations[i].psa_status) {
+ return local_translations[i].mbedtls_error;
+ }
+ }
+ return fallback_f(status);
+}
+
+#if defined(MBEDTLS_PK_C)
+int psa_pk_status_to_mbedtls(psa_status_t status)
+{
+ switch (status) {
+ case PSA_ERROR_INVALID_HANDLE:
+ return MBEDTLS_ERR_PK_KEY_INVALID_FORMAT;
+ case PSA_ERROR_BUFFER_TOO_SMALL:
+ return MBEDTLS_ERR_PK_BUFFER_TOO_SMALL;
+ case PSA_ERROR_NOT_SUPPORTED:
+ return MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE;
+ case PSA_ERROR_INVALID_ARGUMENT:
+ return MBEDTLS_ERR_PK_INVALID_ALG;
+ case PSA_ERROR_NOT_PERMITTED:
+ return MBEDTLS_ERR_PK_TYPE_MISMATCH;
+ case PSA_ERROR_INSUFFICIENT_MEMORY:
+ return MBEDTLS_ERR_PK_ALLOC_FAILED;
+ case PSA_ERROR_BAD_STATE:
+ return MBEDTLS_ERR_PK_BAD_INPUT_DATA;
+ case PSA_ERROR_DATA_CORRUPT:
+ case PSA_ERROR_DATA_INVALID:
+ case PSA_ERROR_STORAGE_FAILURE:
+ return MBEDTLS_ERR_PK_FILE_IO_ERROR;
+ default:
+ return psa_generic_status_to_mbedtls(status);
+ }
+}
+#endif /* MBEDTLS_PK_C */
+
+/****************************************************************/
+/* Key management */
+/****************************************************************/
+
+#if defined(PSA_WANT_KEY_TYPE_ECC_PUBLIC_KEY)
+psa_ecc_family_t mbedtls_ecc_group_to_psa(mbedtls_ecp_group_id grpid,
+ size_t *bits)
+{
+ switch (grpid) {
+#if defined(MBEDTLS_ECP_HAVE_SECP192R1)
+ case MBEDTLS_ECP_DP_SECP192R1:
+ *bits = 192;
+ return PSA_ECC_FAMILY_SECP_R1;
+#endif
+#if defined(MBEDTLS_ECP_HAVE_SECP224R1)
+ case MBEDTLS_ECP_DP_SECP224R1:
+ *bits = 224;
+ return PSA_ECC_FAMILY_SECP_R1;
+#endif
+#if defined(MBEDTLS_ECP_HAVE_SECP256R1)
+ case MBEDTLS_ECP_DP_SECP256R1:
+ *bits = 256;
+ return PSA_ECC_FAMILY_SECP_R1;
+#endif
+#if defined(MBEDTLS_ECP_HAVE_SECP384R1)
+ case MBEDTLS_ECP_DP_SECP384R1:
+ *bits = 384;
+ return PSA_ECC_FAMILY_SECP_R1;
+#endif
+#if defined(MBEDTLS_ECP_HAVE_SECP521R1)
+ case MBEDTLS_ECP_DP_SECP521R1:
+ *bits = 521;
+ return PSA_ECC_FAMILY_SECP_R1;
+#endif
+#if defined(MBEDTLS_ECP_HAVE_BP256R1)
+ case MBEDTLS_ECP_DP_BP256R1:
+ *bits = 256;
+ return PSA_ECC_FAMILY_BRAINPOOL_P_R1;
+#endif
+#if defined(MBEDTLS_ECP_HAVE_BP384R1)
+ case MBEDTLS_ECP_DP_BP384R1:
+ *bits = 384;
+ return PSA_ECC_FAMILY_BRAINPOOL_P_R1;
+#endif
+#if defined(MBEDTLS_ECP_HAVE_BP512R1)
+ case MBEDTLS_ECP_DP_BP512R1:
+ *bits = 512;
+ return PSA_ECC_FAMILY_BRAINPOOL_P_R1;
+#endif
+#if defined(MBEDTLS_ECP_HAVE_CURVE25519)
+ case MBEDTLS_ECP_DP_CURVE25519:
+ *bits = 255;
+ return PSA_ECC_FAMILY_MONTGOMERY;
+#endif
+#if defined(MBEDTLS_ECP_HAVE_SECP192K1)
+ case MBEDTLS_ECP_DP_SECP192K1:
+ *bits = 192;
+ return PSA_ECC_FAMILY_SECP_K1;
+#endif
+#if defined(MBEDTLS_ECP_HAVE_SECP224K1)
+ /* secp224k1 is not and will not be supported in PSA (#3541). */
+#endif
+#if defined(MBEDTLS_ECP_HAVE_SECP256K1)
+ case MBEDTLS_ECP_DP_SECP256K1:
+ *bits = 256;
+ return PSA_ECC_FAMILY_SECP_K1;
+#endif
+#if defined(MBEDTLS_ECP_HAVE_CURVE448)
+ case MBEDTLS_ECP_DP_CURVE448:
+ *bits = 448;
+ return PSA_ECC_FAMILY_MONTGOMERY;
+#endif
+ default:
+ *bits = 0;
+ return 0;
+ }
+}
+
+mbedtls_ecp_group_id mbedtls_ecc_group_from_psa(psa_ecc_family_t family,
+ size_t bits)
+{
+ switch (family) {
+ case PSA_ECC_FAMILY_SECP_R1:
+ switch (bits) {
+#if defined(PSA_WANT_ECC_SECP_R1_192)
+ case 192:
+ return MBEDTLS_ECP_DP_SECP192R1;
+#endif
+#if defined(PSA_WANT_ECC_SECP_R1_224)
+ case 224:
+ return MBEDTLS_ECP_DP_SECP224R1;
+#endif
+#if defined(PSA_WANT_ECC_SECP_R1_256)
+ case 256:
+ return MBEDTLS_ECP_DP_SECP256R1;
+#endif
+#if defined(PSA_WANT_ECC_SECP_R1_384)
+ case 384:
+ return MBEDTLS_ECP_DP_SECP384R1;
+#endif
+#if defined(PSA_WANT_ECC_SECP_R1_521)
+ case 521:
+ return MBEDTLS_ECP_DP_SECP521R1;
+#endif
+ }
+ break;
+
+ case PSA_ECC_FAMILY_BRAINPOOL_P_R1:
+ switch (bits) {
+#if defined(PSA_WANT_ECC_BRAINPOOL_P_R1_256)
+ case 256:
+ return MBEDTLS_ECP_DP_BP256R1;
+#endif
+#if defined(PSA_WANT_ECC_BRAINPOOL_P_R1_384)
+ case 384:
+ return MBEDTLS_ECP_DP_BP384R1;
+#endif
+#if defined(PSA_WANT_ECC_BRAINPOOL_P_R1_512)
+ case 512:
+ return MBEDTLS_ECP_DP_BP512R1;
+#endif
+ }
+ break;
+
+ case PSA_ECC_FAMILY_MONTGOMERY:
+ switch (bits) {
+#if defined(PSA_WANT_ECC_MONTGOMERY_255)
+ case 255:
+ return MBEDTLS_ECP_DP_CURVE25519;
+#endif
+#if defined(PSA_WANT_ECC_MONTGOMERY_448)
+ case 448:
+ return MBEDTLS_ECP_DP_CURVE448;
+#endif
+ }
+ break;
+
+ case PSA_ECC_FAMILY_SECP_K1:
+ switch (bits) {
+#if defined(PSA_WANT_ECC_SECP_K1_192)
+ case 192:
+ return MBEDTLS_ECP_DP_SECP192K1;
+#endif
+#if defined(PSA_WANT_ECC_SECP_K1_224)
+ /* secp224k1 is not and will not be supported in PSA (#3541). */
+#endif
+#if defined(PSA_WANT_ECC_SECP_K1_256)
+ case 256:
+ return MBEDTLS_ECP_DP_SECP256K1;
+#endif
+ }
+ break;
+ }
+
+ return MBEDTLS_ECP_DP_NONE;
+}
+#endif /* PSA_WANT_KEY_TYPE_ECC_PUBLIC_KEY */
+
+/* Wrapper function allowing the classic API to use the PSA RNG.
+ *
+ * `mbedtls_psa_get_random(MBEDTLS_PSA_RANDOM_STATE, ...)` calls
+ * `psa_generate_random(...)`. The state parameter is ignored since the
+ * PSA API doesn't support passing an explicit state.
+ */
+int mbedtls_psa_get_random(void *p_rng,
+ unsigned char *output,
+ size_t output_size)
+{
+ /* This function takes a pointer to the RNG state because that's what
+ * classic mbedtls functions using an RNG expect. The PSA RNG manages
+ * its own state internally and doesn't let the caller access that state.
+ * So we just ignore the state parameter, and in practice we'll pass
+ * NULL. */
+ (void) p_rng;
+ psa_status_t status = psa_generate_random(output, output_size);
+ if (status == PSA_SUCCESS) {
+ return 0;
+ } else {
+ return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED;
+ }
+}
+
+#endif /* MBEDTLS_PSA_CRYPTO_CLIENT */
+
+#if defined(MBEDTLS_PSA_UTIL_HAVE_ECDSA)
+
+/**
+ * \brief Convert a single raw coordinate to DER ASN.1 format. The output der
+ * buffer is filled backward (i.e. starting from its end).
+ *
+ * \param raw_buf Buffer containing the raw coordinate to be
+ * converted.
+ * \param raw_len Length of raw_buf in bytes. This must be > 0.
+ * \param der_buf_start Pointer to the beginning of the buffer which
+ * will be filled with the DER converted data.
+ * \param der_buf_end End of the buffer used to store the DER output.
+ *
+ * \return On success, the amount of data (in bytes) written to
+ * the DER buffer.
+ * \return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL if the provided der
+ * buffer is too small to contain all the converted data.
+ * \return MBEDTLS_ERR_ASN1_INVALID_DATA if the input raw
+ * coordinate is null (i.e. all zeros).
+ *
+ * \warning Raw and der buffer must not be overlapping.
+ */
+static int convert_raw_to_der_single_int(const unsigned char *raw_buf, size_t raw_len,
+ unsigned char *der_buf_start,
+ unsigned char *der_buf_end)
+{
+ unsigned char *p = der_buf_end;
+ int len;
+ int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+
+ /* ASN.1 DER encoding requires minimal length, so skip leading 0s.
+ * Provided input MPIs should not be 0, but as a failsafe measure, still
+ * detect that and return error in case. */
+ while (*raw_buf == 0x00) {
+ ++raw_buf;
+ --raw_len;
+ if (raw_len == 0) {
+ return MBEDTLS_ERR_ASN1_INVALID_DATA;
+ }
+ }
+ len = (int) raw_len;
+
+ /* Copy the raw coordinate to the end of der_buf. */
+ if ((p - der_buf_start) < len) {
+ return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL;
+ }
+ p -= len;
+ memcpy(p, raw_buf, len);
+
+ /* If MSb is 1, ASN.1 requires that we prepend a 0. */
+ if (*p & 0x80) {
+ if ((p - der_buf_start) < 1) {
+ return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL;
+ }
+ --p;
+ *p = 0x00;
+ ++len;
+ }
+
+ MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&p, der_buf_start, len));
+ MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(&p, der_buf_start, MBEDTLS_ASN1_INTEGER));
+
+ return len;
+}
+
+int mbedtls_ecdsa_raw_to_der(size_t bits, const unsigned char *raw, size_t raw_len,
+ unsigned char *der, size_t der_size, size_t *der_len)
+{
+ unsigned char r[PSA_BITS_TO_BYTES(PSA_VENDOR_ECC_MAX_CURVE_BITS)];
+ unsigned char s[PSA_BITS_TO_BYTES(PSA_VENDOR_ECC_MAX_CURVE_BITS)];
+ const size_t coordinate_len = PSA_BITS_TO_BYTES(bits);
+ size_t len = 0;
+ unsigned char *p = der + der_size;
+ int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+
+ if (raw_len != (2 * coordinate_len)) {
+ return MBEDTLS_ERR_ASN1_INVALID_DATA;
+ }
+ if (coordinate_len > sizeof(r)) {
+ return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL;
+ }
+
+ /* Since raw and der buffers might overlap, dump r and s before starting
+ * the conversion. */
+ memcpy(r, raw, coordinate_len);
+ memcpy(s, raw + coordinate_len, coordinate_len);
+
+ /* der buffer will initially be written starting from its end so we pick s
+ * first and then r. */
+ ret = convert_raw_to_der_single_int(s, coordinate_len, der, p);
+ if (ret < 0) {
+ return ret;
+ }
+ p -= ret;
+ len += ret;
+
+ ret = convert_raw_to_der_single_int(r, coordinate_len, der, p);
+ if (ret < 0) {
+ return ret;
+ }
+ p -= ret;
+ len += ret;
+
+ /* Add ASN.1 header (len + tag). */
+ MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&p, der, len));
+ MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(&p, der,
+ MBEDTLS_ASN1_CONSTRUCTED |
+ MBEDTLS_ASN1_SEQUENCE));
+
+ /* memmove the content of der buffer to its beginnig. */
+ memmove(der, p, len);
+ *der_len = len;
+
+ return 0;
+}
+
+/**
+ * \brief Convert a single integer from ASN.1 DER format to raw.
+ *
+ * \param der Buffer containing the DER integer value to be
+ * converted.
+ * \param der_len Length of the der buffer in bytes.
+ * \param raw Output buffer that will be filled with the
+ * converted data. This should be at least
+ * coordinate_size bytes and it must be zeroed before
+ * calling this function.
+ * \param coordinate_size Size (in bytes) of a single coordinate in raw
+ * format.
+ *
+ * \return On success, the amount of DER data parsed from the
+ * provided der buffer.
+ * \return MBEDTLS_ERR_ASN1_UNEXPECTED_TAG if the integer tag
+ * is missing in the der buffer.
+ * \return MBEDTLS_ERR_ASN1_LENGTH_MISMATCH if the integer
+ * is null (i.e. all zeros) or if the output raw buffer
+ * is too small to contain the converted raw value.
+ *
+ * \warning Der and raw buffers must not be overlapping.
+ */
+static int convert_der_to_raw_single_int(unsigned char *der, size_t der_len,
+ unsigned char *raw, size_t coordinate_size)
+{
+ unsigned char *p = der;
+ int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
+ size_t unpadded_len, padding_len = 0;
+
+ /* Get the length of ASN.1 element (i.e. the integer we need to parse). */
+ ret = mbedtls_asn1_get_tag(&p, p + der_len, &unpadded_len,
+ MBEDTLS_ASN1_INTEGER);
+ if (ret != 0) {
+ return ret;
+ }
+
+ /* It's invalid to have:
+ * - unpadded_len == 0.
+ * - MSb set without a leading 0x00 (leading 0x00 is checked below). */
+ if (((unpadded_len == 0) || (*p & 0x80) != 0)) {
+ return MBEDTLS_ERR_ASN1_INVALID_DATA;
+ }
+
+ /* Skip possible leading zero */
+ if (*p == 0x00) {
+ p++;
+ unpadded_len--;
+ /* It is not allowed to have more than 1 leading zero.
+ * Ignore the case in which unpadded_len = 0 because that's a 0 encoded
+ * in ASN.1 format (i.e. 020100). */
+ if ((unpadded_len > 0) && (*p == 0x00)) {
+ return MBEDTLS_ERR_ASN1_INVALID_DATA;
+ }
+ }
+
+ if (unpadded_len > coordinate_size) {
+ /* Parsed number is longer than the maximum expected value. */
+ return MBEDTLS_ERR_ASN1_INVALID_DATA;
+ }
+ padding_len = coordinate_size - unpadded_len;
+ /* raw buffer was already zeroed by the calling function so zero-padding
+ * operation is skipped here. */
+ memcpy(raw + padding_len, p, unpadded_len);
+ p += unpadded_len;
+
+ return (int) (p - der);
+}
+
+int mbedtls_ecdsa_der_to_raw(size_t bits, const unsigned char *der, size_t der_len,
+ unsigned char *raw, size_t raw_size, size_t *raw_len)
+{
+ unsigned char raw_tmp[PSA_VENDOR_ECDSA_SIGNATURE_MAX_SIZE];
+ unsigned char *p = (unsigned char *) der;
+ size_t data_len;
+ size_t coordinate_size = PSA_BITS_TO_BYTES(bits);
+ int ret;
+
+ /* The output raw buffer should be at least twice the size of a raw
+ * coordinate in order to store r and s. */
+ if (raw_size < coordinate_size * 2) {
+ return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL;
+ }
+ if (2 * coordinate_size > sizeof(raw_tmp)) {
+ return MBEDTLS_ERR_ASN1_BUF_TOO_SMALL;
+ }
+
+ /* Check that the provided input DER buffer has the right header. */
+ ret = mbedtls_asn1_get_tag(&p, der + der_len, &data_len,
+ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
+ if (ret != 0) {
+ return ret;
+ }
+
+ memset(raw_tmp, 0, 2 * coordinate_size);
+
+ /* Extract r */
+ ret = convert_der_to_raw_single_int(p, data_len, raw_tmp, coordinate_size);
+ if (ret < 0) {
+ return ret;
+ }
+ p += ret;
+ data_len -= ret;
+
+ /* Extract s */
+ ret = convert_der_to_raw_single_int(p, data_len, raw_tmp + coordinate_size,
+ coordinate_size);
+ if (ret < 0) {
+ return ret;
+ }
+ p += ret;
+ data_len -= ret;
+
+ /* Check that we consumed all the input der data. */
+ if ((size_t) (p - der) != der_len) {
+ return MBEDTLS_ERR_ASN1_LENGTH_MISMATCH;
+ }
+
+ memcpy(raw, raw_tmp, 2 * coordinate_size);
+ *raw_len = 2 * coordinate_size;
+
+ return 0;
+}
+
+#endif /* MBEDTLS_PSA_UTIL_HAVE_ECDSA */
diff --git a/thirdparty/misc/bcdec.h b/thirdparty/misc/bcdec.h
new file mode 100644
index 0000000000..78074a0c90
--- /dev/null
+++ b/thirdparty/misc/bcdec.h
@@ -0,0 +1,1329 @@
+/* bcdec.h - v0.96
+ provides functions to decompress blocks of BC compressed images
+ written by Sergii "iOrange" Kudlai in 2022
+
+ This library does not allocate memory and is trying to use as less stack as possible
+
+ The library was never optimized specifically for speed but for the overall size
+ it has zero external dependencies and is not using any runtime functions
+
+ Supported BC formats:
+ BC1 (also known as DXT1) + it's "binary alpha" variant BC1A (DXT1A)
+ BC2 (also known as DXT3)
+ BC3 (also known as DXT5)
+ BC4 (also known as ATI1N)
+ BC5 (also known as ATI2N)
+ BC6H (HDR format)
+ BC7
+
+ BC1/BC2/BC3/BC7 are expected to decompress into 4*4 RGBA blocks 8bit per component (32bit pixel)
+ BC4/BC5 are expected to decompress into 4*4 R/RG blocks 8bit per component (8bit and 16bit pixel)
+ BC6H is expected to decompress into 4*4 RGB blocks of either 32bit float or 16bit "half" per
+ component (96bit or 48bit pixel)
+
+ For more info, issues and suggestions please visit https://github.com/iOrange/bcdec
+
+ CREDITS:
+ Aras Pranckevicius (@aras-p) - BC1/BC3 decoders optimizations (up to 3x the speed)
+ - BC6H/BC7 bits pulling routines optimizations
+ - optimized BC6H by moving unquantize out of the loop
+ - Split BC6H decompression function into 'half' and
+ 'float' variants
+
+ bugfixes:
+ @linkmauve
+
+ LICENSE: See end of file for license information.
+*/
+
+#ifndef BCDEC_HEADER_INCLUDED
+#define BCDEC_HEADER_INCLUDED
+
+/* if BCDEC_STATIC causes problems, try defining BCDECDEF to 'inline' or 'static inline' */
+#ifndef BCDECDEF
+#ifdef BCDEC_STATIC
+#define BCDECDEF static
+#else
+#ifdef __cplusplus
+#define BCDECDEF extern "C"
+#else
+#define BCDECDEF extern
+#endif
+#endif
+#endif
+
+/* Used information sources:
+ https://docs.microsoft.com/en-us/windows/win32/direct3d10/d3d10-graphics-programming-guide-resources-block-compression
+ https://docs.microsoft.com/en-us/windows/win32/direct3d11/bc6h-format
+ https://docs.microsoft.com/en-us/windows/win32/direct3d11/bc7-format
+ https://docs.microsoft.com/en-us/windows/win32/direct3d11/bc7-format-mode-reference
+
+ ! WARNING ! Khronos's BPTC partitions tables contain mistakes, do not use them!
+ https://www.khronos.org/registry/DataFormat/specs/1.1/dataformat.1.1.html#BPTC
+
+ ! Use tables from here instead !
+ https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_texture_compression_bptc.txt
+
+ Leaving it here as it's a nice read
+ https://fgiesen.wordpress.com/2021/10/04/gpu-bcn-decoding/
+
+ Fast half to float function from here
+ https://gist.github.com/rygorous/2144712
+*/
+
+#define BCDEC_BC1_BLOCK_SIZE 8
+#define BCDEC_BC2_BLOCK_SIZE 16
+#define BCDEC_BC3_BLOCK_SIZE 16
+#define BCDEC_BC4_BLOCK_SIZE 8
+#define BCDEC_BC5_BLOCK_SIZE 16
+#define BCDEC_BC6H_BLOCK_SIZE 16
+#define BCDEC_BC7_BLOCK_SIZE 16
+
+#define BCDEC_BC1_COMPRESSED_SIZE(w, h) ((((w)>>2)*((h)>>2))*BCDEC_BC1_BLOCK_SIZE)
+#define BCDEC_BC2_COMPRESSED_SIZE(w, h) ((((w)>>2)*((h)>>2))*BCDEC_BC2_BLOCK_SIZE)
+#define BCDEC_BC3_COMPRESSED_SIZE(w, h) ((((w)>>2)*((h)>>2))*BCDEC_BC3_BLOCK_SIZE)
+#define BCDEC_BC4_COMPRESSED_SIZE(w, h) ((((w)>>2)*((h)>>2))*BCDEC_BC4_BLOCK_SIZE)
+#define BCDEC_BC5_COMPRESSED_SIZE(w, h) ((((w)>>2)*((h)>>2))*BCDEC_BC5_BLOCK_SIZE)
+#define BCDEC_BC6H_COMPRESSED_SIZE(w, h) ((((w)>>2)*((h)>>2))*BCDEC_BC6H_BLOCK_SIZE)
+#define BCDEC_BC7_COMPRESSED_SIZE(w, h) ((((w)>>2)*((h)>>2))*BCDEC_BC7_BLOCK_SIZE)
+
+BCDECDEF void bcdec_bc1(const void* compressedBlock, void* decompressedBlock, int destinationPitch);
+BCDECDEF void bcdec_bc2(const void* compressedBlock, void* decompressedBlock, int destinationPitch);
+BCDECDEF void bcdec_bc3(const void* compressedBlock, void* decompressedBlock, int destinationPitch);
+BCDECDEF void bcdec_bc4(const void* compressedBlock, void* decompressedBlock, int destinationPitch);
+BCDECDEF void bcdec_bc5(const void* compressedBlock, void* decompressedBlock, int destinationPitch);
+BCDECDEF void bcdec_bc6h_float(const void* compressedBlock, void* decompressedBlock, int destinationPitch, int isSigned);
+BCDECDEF void bcdec_bc6h_half(const void* compressedBlock, void* decompressedBlock, int destinationPitch, int isSigned);
+BCDECDEF void bcdec_bc7(const void* compressedBlock, void* decompressedBlock, int destinationPitch);
+
+
+#ifdef BCDEC_IMPLEMENTATION
+
+static void bcdec__color_block(const void* compressedBlock, void* decompressedBlock, int destinationPitch, int onlyOpaqueMode) {
+ unsigned short c0, c1;
+ unsigned int refColors[4]; /* 0xAABBGGRR */
+ unsigned char* dstColors;
+ unsigned int colorIndices;
+ int i, j, idx;
+ unsigned int r0, g0, b0, r1, g1, b1, r, g, b;
+
+ c0 = ((unsigned short*)compressedBlock)[0];
+ c1 = ((unsigned short*)compressedBlock)[1];
+
+ /* Expand 565 ref colors to 888 */
+ r0 = (((c0 >> 11) & 0x1F) * 527 + 23) >> 6;
+ g0 = (((c0 >> 5) & 0x3F) * 259 + 33) >> 6;
+ b0 = ((c0 & 0x1F) * 527 + 23) >> 6;
+ refColors[0] = 0xFF000000 | (b0 << 16) | (g0 << 8) | r0;
+
+ r1 = (((c1 >> 11) & 0x1F) * 527 + 23) >> 6;
+ g1 = (((c1 >> 5) & 0x3F) * 259 + 33) >> 6;
+ b1 = ((c1 & 0x1F) * 527 + 23) >> 6;
+ refColors[1] = 0xFF000000 | (b1 << 16) | (g1 << 8) | r1;
+
+ if (c0 > c1 || onlyOpaqueMode) { /* Standard BC1 mode (also BC3 color block uses ONLY this mode) */
+ /* color_2 = 2/3*color_0 + 1/3*color_1
+ color_3 = 1/3*color_0 + 2/3*color_1 */
+ r = (2 * r0 + r1 + 1) / 3;
+ g = (2 * g0 + g1 + 1) / 3;
+ b = (2 * b0 + b1 + 1) / 3;
+ refColors[2] = 0xFF000000 | (b << 16) | (g << 8) | r;
+
+ r = (r0 + 2 * r1 + 1) / 3;
+ g = (g0 + 2 * g1 + 1) / 3;
+ b = (b0 + 2 * b1 + 1) / 3;
+ refColors[3] = 0xFF000000 | (b << 16) | (g << 8) | r;
+ } else { /* Quite rare BC1A mode */
+ /* color_2 = 1/2*color_0 + 1/2*color_1;
+ color_3 = 0; */
+ r = (r0 + r1 + 1) >> 1;
+ g = (g0 + g1 + 1) >> 1;
+ b = (b0 + b1 + 1) >> 1;
+ refColors[2] = 0xFF000000 | (b << 16) | (g << 8) | r;
+
+ refColors[3] = 0x00000000;
+ }
+
+ colorIndices = ((unsigned int*)compressedBlock)[1];
+
+ /* Fill out the decompressed color block */
+ dstColors = (unsigned char*)decompressedBlock;
+ for (i = 0; i < 4; ++i) {
+ for (j = 0; j < 4; ++j) {
+ idx = colorIndices & 0x03;
+ ((unsigned int*)dstColors)[j] = refColors[idx];
+ colorIndices >>= 2;
+ }
+
+ dstColors += destinationPitch;
+ }
+}
+
+static void bcdec__sharp_alpha_block(const void* compressedBlock, void* decompressedBlock, int destinationPitch) {
+ unsigned short* alpha;
+ unsigned char* decompressed;
+ int i, j;
+
+ alpha = (unsigned short*)compressedBlock;
+ decompressed = (unsigned char*)decompressedBlock;
+
+ for (i = 0; i < 4; ++i) {
+ for (j = 0; j < 4; ++j) {
+ decompressed[j * 4] = ((alpha[i] >> (4 * j)) & 0x0F) * 17;
+ }
+
+ decompressed += destinationPitch;
+ }
+}
+
+static void bcdec__smooth_alpha_block(const void* compressedBlock, void* decompressedBlock, int destinationPitch, int pixelSize) {
+ unsigned char* decompressed;
+ unsigned char alpha[8];
+ int i, j;
+ unsigned long long block, indices;
+
+ block = *(unsigned long long*)compressedBlock;
+ decompressed = (unsigned char*)decompressedBlock;
+
+ alpha[0] = block & 0xFF;
+ alpha[1] = (block >> 8) & 0xFF;
+
+ if (alpha[0] > alpha[1]) {
+ /* 6 interpolated alpha values. */
+ alpha[2] = (6 * alpha[0] + alpha[1] + 1) / 7; /* 6/7*alpha_0 + 1/7*alpha_1 */
+ alpha[3] = (5 * alpha[0] + 2 * alpha[1] + 1) / 7; /* 5/7*alpha_0 + 2/7*alpha_1 */
+ alpha[4] = (4 * alpha[0] + 3 * alpha[1] + 1) / 7; /* 4/7*alpha_0 + 3/7*alpha_1 */
+ alpha[5] = (3 * alpha[0] + 4 * alpha[1] + 1) / 7; /* 3/7*alpha_0 + 4/7*alpha_1 */
+ alpha[6] = (2 * alpha[0] + 5 * alpha[1] + 1) / 7; /* 2/7*alpha_0 + 5/7*alpha_1 */
+ alpha[7] = ( alpha[0] + 6 * alpha[1] + 1) / 7; /* 1/7*alpha_0 + 6/7*alpha_1 */
+ }
+ else {
+ /* 4 interpolated alpha values. */
+ alpha[2] = (4 * alpha[0] + alpha[1] + 1) / 5; /* 4/5*alpha_0 + 1/5*alpha_1 */
+ alpha[3] = (3 * alpha[0] + 2 * alpha[1] + 1) / 5; /* 3/5*alpha_0 + 2/5*alpha_1 */
+ alpha[4] = (2 * alpha[0] + 3 * alpha[1] + 1) / 5; /* 2/5*alpha_0 + 3/5*alpha_1 */
+ alpha[5] = ( alpha[0] + 4 * alpha[1] + 1) / 5; /* 1/5*alpha_0 + 4/5*alpha_1 */
+ alpha[6] = 0x00;
+ alpha[7] = 0xFF;
+ }
+
+ indices = block >> 16;
+ for (i = 0; i < 4; ++i) {
+ for (j = 0; j < 4; ++j) {
+ decompressed[j * pixelSize] = alpha[indices & 0x07];
+ indices >>= 3;
+ }
+
+ decompressed += destinationPitch;
+ }
+}
+
+typedef struct bcdec__bitstream {
+ unsigned long long low;
+ unsigned long long high;
+} bcdec__bitstream_t;
+
+static int bcdec__bitstream_read_bits(bcdec__bitstream_t* bstream, int numBits) {
+ unsigned int mask = (1 << numBits) - 1;
+ /* Read the low N bits */
+ unsigned int bits = (bstream->low & mask);
+
+ bstream->low >>= numBits;
+ /* Put the low N bits of "high" into the high 64-N bits of "low". */
+ bstream->low |= (bstream->high & mask) << (sizeof(bstream->high) * 8 - numBits);
+ bstream->high >>= numBits;
+
+ return bits;
+}
+
+static int bcdec__bitstream_read_bit(bcdec__bitstream_t* bstream) {
+ return bcdec__bitstream_read_bits(bstream, 1);
+}
+
+/* reversed bits pulling, used in BC6H decoding
+ why ?? just why ??? */
+static int bcdec__bitstream_read_bits_r(bcdec__bitstream_t* bstream, int numBits) {
+ int bits = bcdec__bitstream_read_bits(bstream, numBits);
+ /* Reverse the bits. */
+ int result = 0;
+ while (numBits--) {
+ result <<= 1;
+ result |= (bits & 1);
+ bits >>= 1;
+ }
+ return result;
+}
+
+
+
+BCDECDEF void bcdec_bc1(const void* compressedBlock, void* decompressedBlock, int destinationPitch) {
+ bcdec__color_block(compressedBlock, decompressedBlock, destinationPitch, 0);
+}
+
+BCDECDEF void bcdec_bc2(const void* compressedBlock, void* decompressedBlock, int destinationPitch) {
+ bcdec__color_block(((char*)compressedBlock) + 8, decompressedBlock, destinationPitch, 1);
+ bcdec__sharp_alpha_block(compressedBlock, ((char*)decompressedBlock) + 3, destinationPitch);
+}
+
+BCDECDEF void bcdec_bc3(const void* compressedBlock, void* decompressedBlock, int destinationPitch) {
+ bcdec__color_block(((char*)compressedBlock) + 8, decompressedBlock, destinationPitch, 1);
+ bcdec__smooth_alpha_block(compressedBlock, ((char*)decompressedBlock) + 3, destinationPitch, 4);
+}
+
+BCDECDEF void bcdec_bc4(const void* compressedBlock, void* decompressedBlock, int destinationPitch) {
+ bcdec__smooth_alpha_block(compressedBlock, decompressedBlock, destinationPitch, 1);
+}
+
+BCDECDEF void bcdec_bc5(const void* compressedBlock, void* decompressedBlock, int destinationPitch) {
+ bcdec__smooth_alpha_block(compressedBlock, decompressedBlock, destinationPitch, 2);
+ bcdec__smooth_alpha_block(((char*)compressedBlock) + 8, ((char*)decompressedBlock) + 1, destinationPitch, 2);
+}
+
+/* http://graphics.stanford.edu/~seander/bithacks.html#VariableSignExtend */
+static int bcdec__extend_sign(int val, int bits) {
+ return (val << (32 - bits)) >> (32 - bits);
+}
+
+static int bcdec__transform_inverse(int val, int a0, int bits, int isSigned) {
+ /* If the precision of A0 is "p" bits, then the transform algorithm is:
+ B0 = (B0 + A0) & ((1 << p) - 1) */
+ val = (val + a0) & ((1 << bits) - 1);
+ if (isSigned) {
+ val = bcdec__extend_sign(val, bits);
+ }
+ return val;
+}
+
+/* pretty much copy-paste from documentation */
+static int bcdec__unquantize(int val, int bits, int isSigned) {
+ int unq, s = 0;
+
+ if (!isSigned) {
+ if (bits >= 15) {
+ unq = val;
+ } else if (!val) {
+ unq = 0;
+ } else if (val == ((1 << bits) - 1)) {
+ unq = 0xFFFF;
+ } else {
+ unq = ((val << 16) + 0x8000) >> bits;
+ }
+ } else {
+ if (bits >= 16) {
+ unq = val;
+ } else {
+ if (val < 0) {
+ s = 1;
+ val = -val;
+ }
+
+ if (val == 0) {
+ unq = 0;
+ } else if (val >= ((1 << (bits - 1)) - 1)) {
+ unq = 0x7FFF;
+ } else {
+ unq = ((val << 15) + 0x4000) >> (bits - 1);
+ }
+
+ if (s) {
+ unq = -unq;
+ }
+ }
+ }
+ return unq;
+}
+
+static int bcdec__interpolate(int a, int b, int* weights, int index) {
+ return (a * (64 - weights[index]) + b * weights[index] + 32) >> 6;
+}
+
+static unsigned short bcdec__finish_unquantize(int val, int isSigned) {
+ int s;
+
+ if (!isSigned) {
+ return (unsigned short)((val * 31) >> 6); /* scale the magnitude by 31 / 64 */
+ } else {
+ val = (val < 0) ? -(((-val) * 31) >> 5) : (val * 31) >> 5; /* scale the magnitude by 31 / 32 */
+ s = 0;
+ if (val < 0) {
+ s = 0x8000;
+ val = -val;
+ }
+ return (unsigned short)(s | val);
+ }
+}
+
+/* modified half_to_float_fast4 from https://gist.github.com/rygorous/2144712 */
+static float bcdec__half_to_float_quick(unsigned short half) {
+ typedef union {
+ unsigned int u;
+ float f;
+ } FP32;
+
+ static const FP32 magic = { 113 << 23 };
+ static const unsigned int shifted_exp = 0x7c00 << 13; /* exponent mask after shift */
+ FP32 o;
+ unsigned int exp;
+
+ o.u = (half & 0x7fff) << 13; /* exponent/mantissa bits */
+ exp = shifted_exp & o.u; /* just the exponent */
+ o.u += (127 - 15) << 23; /* exponent adjust */
+
+ /* handle exponent special cases */
+ if (exp == shifted_exp) { /* Inf/NaN? */
+ o.u += (128 - 16) << 23; /* extra exp adjust */
+ } else if (exp == 0) { /* Zero/Denormal? */
+ o.u += 1 << 23; /* extra exp adjust */
+ o.f -= magic.f; /* renormalize */
+ }
+
+ o.u |= (half & 0x8000) << 16; /* sign bit */
+ return o.f;
+}
+
+BCDECDEF void bcdec_bc6h_half(const void* compressedBlock, void* decompressedBlock, int destinationPitch, int isSigned) {
+ static char actual_bits_count[4][14] = {
+ { 10, 7, 11, 11, 11, 9, 8, 8, 8, 6, 10, 11, 12, 16 }, /* W */
+ { 5, 6, 5, 4, 4, 5, 6, 5, 5, 6, 10, 9, 8, 4 }, /* dR */
+ { 5, 6, 4, 5, 4, 5, 5, 6, 5, 6, 10, 9, 8, 4 }, /* dG */
+ { 5, 6, 4, 4, 5, 5, 5, 5, 6, 6, 10, 9, 8, 4 } /* dB */
+ };
+
+ /* There are 32 possible partition sets for a two-region tile.
+ Each 4x4 block represents a single shape.
+ Here also every fix-up index has MSB bit set. */
+ static unsigned char partition_sets[32][4][4] = {
+ { {128, 0, 1, 1}, {0, 0, 1, 1}, { 0, 0, 1, 1}, {0, 0, 1, 129} }, /* 0 */
+ { {128, 0, 0, 1}, {0, 0, 0, 1}, { 0, 0, 0, 1}, {0, 0, 0, 129} }, /* 1 */
+ { {128, 1, 1, 1}, {0, 1, 1, 1}, { 0, 1, 1, 1}, {0, 1, 1, 129} }, /* 2 */
+ { {128, 0, 0, 1}, {0, 0, 1, 1}, { 0, 0, 1, 1}, {0, 1, 1, 129} }, /* 3 */
+ { {128, 0, 0, 0}, {0, 0, 0, 1}, { 0, 0, 0, 1}, {0, 0, 1, 129} }, /* 4 */
+ { {128, 0, 1, 1}, {0, 1, 1, 1}, { 0, 1, 1, 1}, {1, 1, 1, 129} }, /* 5 */
+ { {128, 0, 0, 1}, {0, 0, 1, 1}, { 0, 1, 1, 1}, {1, 1, 1, 129} }, /* 6 */
+ { {128, 0, 0, 0}, {0, 0, 0, 1}, { 0, 0, 1, 1}, {0, 1, 1, 129} }, /* 7 */
+ { {128, 0, 0, 0}, {0, 0, 0, 0}, { 0, 0, 0, 1}, {0, 0, 1, 129} }, /* 8 */
+ { {128, 0, 1, 1}, {0, 1, 1, 1}, { 1, 1, 1, 1}, {1, 1, 1, 129} }, /* 9 */
+ { {128, 0, 0, 0}, {0, 0, 0, 1}, { 0, 1, 1, 1}, {1, 1, 1, 129} }, /* 10 */
+ { {128, 0, 0, 0}, {0, 0, 0, 0}, { 0, 0, 0, 1}, {0, 1, 1, 129} }, /* 11 */
+ { {128, 0, 0, 1}, {0, 1, 1, 1}, { 1, 1, 1, 1}, {1, 1, 1, 129} }, /* 12 */
+ { {128, 0, 0, 0}, {0, 0, 0, 0}, { 1, 1, 1, 1}, {1, 1, 1, 129} }, /* 13 */
+ { {128, 0, 0, 0}, {1, 1, 1, 1}, { 1, 1, 1, 1}, {1, 1, 1, 129} }, /* 14 */
+ { {128, 0, 0, 0}, {0, 0, 0, 0}, { 0, 0, 0, 0}, {1, 1, 1, 129} }, /* 15 */
+ { {128, 0, 0, 0}, {1, 0, 0, 0}, { 1, 1, 1, 0}, {1, 1, 1, 129} }, /* 16 */
+ { {128, 1, 129, 1}, {0, 0, 0, 1}, { 0, 0, 0, 0}, {0, 0, 0, 0} }, /* 17 */
+ { {128, 0, 0, 0}, {0, 0, 0, 0}, {129, 0, 0, 0}, {1, 1, 1, 0} }, /* 18 */
+ { {128, 1, 129, 1}, {0, 0, 1, 1}, { 0, 0, 0, 1}, {0, 0, 0, 0} }, /* 19 */
+ { {128, 0, 129, 1}, {0, 0, 0, 1}, { 0, 0, 0, 0}, {0, 0, 0, 0} }, /* 20 */
+ { {128, 0, 0, 0}, {1, 0, 0, 0}, {129, 1, 0, 0}, {1, 1, 1, 0} }, /* 21 */
+ { {128, 0, 0, 0}, {0, 0, 0, 0}, {129, 0, 0, 0}, {1, 1, 0, 0} }, /* 22 */
+ { {128, 1, 1, 1}, {0, 0, 1, 1}, { 0, 0, 1, 1}, {0, 0, 0, 129} }, /* 23 */
+ { {128, 0, 129, 1}, {0, 0, 0, 1}, { 0, 0, 0, 1}, {0, 0, 0, 0} }, /* 24 */
+ { {128, 0, 0, 0}, {1, 0, 0, 0}, {129, 0, 0, 0}, {1, 1, 0, 0} }, /* 25 */
+ { {128, 1, 129, 0}, {0, 1, 1, 0}, { 0, 1, 1, 0}, {0, 1, 1, 0} }, /* 26 */
+ { {128, 0, 129, 1}, {0, 1, 1, 0}, { 0, 1, 1, 0}, {1, 1, 0, 0} }, /* 27 */
+ { {128, 0, 0, 1}, {0, 1, 1, 1}, {129, 1, 1, 0}, {1, 0, 0, 0} }, /* 28 */
+ { {128, 0, 0, 0}, {1, 1, 1, 1}, {129, 1, 1, 1}, {0, 0, 0, 0} }, /* 29 */
+ { {128, 1, 129, 1}, {0, 0, 0, 1}, { 1, 0, 0, 0}, {1, 1, 1, 0} }, /* 30 */
+ { {128, 0, 129, 1}, {1, 0, 0, 1}, { 1, 0, 0, 1}, {1, 1, 0, 0} } /* 31 */
+ };
+
+ static int aWeight3[8] = { 0, 9, 18, 27, 37, 46, 55, 64 };
+ static int aWeight4[16] = { 0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 60, 64 };
+
+ bcdec__bitstream_t bstream;
+ int mode, partition, numPartitions, i, j, partitionSet, indexBits, index, ep_i, actualBits0Mode;
+ int r[4], g[4], b[4]; /* wxyz */
+ unsigned short* decompressed;
+ int* weights;
+
+ decompressed = (unsigned short*)decompressedBlock;
+
+ bstream.low = ((unsigned long long*)compressedBlock)[0];
+ bstream.high = ((unsigned long long*)compressedBlock)[1];
+
+ r[0] = r[1] = r[2] = r[3] = 0;
+ g[0] = g[1] = g[2] = g[3] = 0;
+ b[0] = b[1] = b[2] = b[3] = 0;
+
+ mode = bcdec__bitstream_read_bits(&bstream, 2);
+ if (mode > 1) {
+ mode |= (bcdec__bitstream_read_bits(&bstream, 3) << 2);
+ }
+
+ /* modes >= 11 (10 in my code) are using 0 one, others will read it from the bitstream */
+ partition = 0;
+
+ switch (mode) {
+ /* mode 1 */
+ case 0b00: {
+ /* Partitition indices: 46 bits
+ Partition: 5 bits
+ Color Endpoints: 75 bits (10.555, 10.555, 10.555) */
+ g[2] |= bcdec__bitstream_read_bit(&bstream) << 4; /* gy[4] */
+ b[2] |= bcdec__bitstream_read_bit(&bstream) << 4; /* by[4] */
+ b[3] |= bcdec__bitstream_read_bit(&bstream) << 4; /* bz[4] */
+ r[0] |= bcdec__bitstream_read_bits(&bstream, 10); /* rw[9:0] */
+ g[0] |= bcdec__bitstream_read_bits(&bstream, 10); /* gw[9:0] */
+ b[0] |= bcdec__bitstream_read_bits(&bstream, 10); /* bw[9:0] */
+ r[1] |= bcdec__bitstream_read_bits(&bstream, 5); /* rx[4:0] */
+ g[3] |= bcdec__bitstream_read_bit(&bstream) << 4; /* gz[4] */
+ g[2] |= bcdec__bitstream_read_bits(&bstream, 4); /* gy[3:0] */
+ g[1] |= bcdec__bitstream_read_bits(&bstream, 5); /* gx[4:0] */
+ b[3] |= bcdec__bitstream_read_bit(&bstream); /* bz[0] */
+ g[3] |= bcdec__bitstream_read_bits(&bstream, 4); /* gz[3:0] */
+ b[1] |= bcdec__bitstream_read_bits(&bstream, 5); /* bx[4:0] */
+ b[3] |= bcdec__bitstream_read_bit(&bstream) << 1; /* bz[1] */
+ b[2] |= bcdec__bitstream_read_bits(&bstream, 4); /* by[3:0] */
+ r[2] |= bcdec__bitstream_read_bits(&bstream, 5); /* ry[4:0] */
+ b[3] |= bcdec__bitstream_read_bit(&bstream) << 2; /* bz[2] */
+ r[3] |= bcdec__bitstream_read_bits(&bstream, 5); /* rz[4:0] */
+ b[3] |= bcdec__bitstream_read_bit(&bstream) << 3; /* bz[3] */
+ partition = bcdec__bitstream_read_bits(&bstream, 5); /* d[4:0] */
+ mode = 0;
+ } break;
+
+ /* mode 2 */
+ case 0b01: {
+ /* Partitition indices: 46 bits
+ Partition: 5 bits
+ Color Endpoints: 75 bits (7666, 7666, 7666) */
+ g[2] |= bcdec__bitstream_read_bit(&bstream) << 5; /* gy[5] */
+ g[3] |= bcdec__bitstream_read_bit(&bstream) << 4; /* gz[4] */
+ g[3] |= bcdec__bitstream_read_bit(&bstream) << 5; /* gz[5] */
+ r[0] |= bcdec__bitstream_read_bits(&bstream, 7); /* rw[6:0] */
+ b[3] |= bcdec__bitstream_read_bit(&bstream); /* bz[0] */
+ b[3] |= bcdec__bitstream_read_bit(&bstream) << 1; /* bz[1] */
+ b[2] |= bcdec__bitstream_read_bit(&bstream) << 4; /* by[4] */
+ g[0] |= bcdec__bitstream_read_bits(&bstream, 7); /* gw[6:0] */
+ b[2] |= bcdec__bitstream_read_bit(&bstream) << 5; /* by[5] */
+ b[3] |= bcdec__bitstream_read_bit(&bstream) << 2; /* bz[2] */
+ g[2] |= bcdec__bitstream_read_bit(&bstream) << 4; /* gy[4] */
+ b[0] |= bcdec__bitstream_read_bits(&bstream, 7); /* bw[6:0] */
+ b[3] |= bcdec__bitstream_read_bit(&bstream) << 3; /* bz[3] */
+ b[3] |= bcdec__bitstream_read_bit(&bstream) << 5; /* bz[5] */
+ b[3] |= bcdec__bitstream_read_bit(&bstream) << 4; /* bz[4] */
+ r[1] |= bcdec__bitstream_read_bits(&bstream, 6); /* rx[5:0] */
+ g[2] |= bcdec__bitstream_read_bits(&bstream, 4); /* gy[3:0] */
+ g[1] |= bcdec__bitstream_read_bits(&bstream, 6); /* gx[5:0] */
+ g[3] |= bcdec__bitstream_read_bits(&bstream, 4); /* gz[3:0] */
+ b[1] |= bcdec__bitstream_read_bits(&bstream, 6); /* bx[5:0] */
+ b[2] |= bcdec__bitstream_read_bits(&bstream, 4); /* by[3:0] */
+ r[2] |= bcdec__bitstream_read_bits(&bstream, 6); /* ry[5:0] */
+ r[3] |= bcdec__bitstream_read_bits(&bstream, 6); /* rz[5:0] */
+ partition = bcdec__bitstream_read_bits(&bstream, 5); /* d[4:0] */
+ mode = 1;
+ } break;
+
+ /* mode 3 */
+ case 0b00010: {
+ /* Partitition indices: 46 bits
+ Partition: 5 bits
+ Color Endpoints: 72 bits (11.555, 11.444, 11.444) */
+ r[0] |= bcdec__bitstream_read_bits(&bstream, 10); /* rw[9:0] */
+ g[0] |= bcdec__bitstream_read_bits(&bstream, 10); /* gw[9:0] */
+ b[0] |= bcdec__bitstream_read_bits(&bstream, 10); /* bw[9:0] */
+ r[1] |= bcdec__bitstream_read_bits(&bstream, 5); /* rx[4:0] */
+ r[0] |= bcdec__bitstream_read_bit(&bstream) << 10; /* rw[10] */
+ g[2] |= bcdec__bitstream_read_bits(&bstream, 4); /* gy[3:0] */
+ g[1] |= bcdec__bitstream_read_bits(&bstream, 4); /* gx[3:0] */
+ g[0] |= bcdec__bitstream_read_bit(&bstream) << 10; /* gw[10] */
+ b[3] |= bcdec__bitstream_read_bit(&bstream); /* bz[0] */
+ g[3] |= bcdec__bitstream_read_bits(&bstream, 4); /* gz[3:0] */
+ b[1] |= bcdec__bitstream_read_bits(&bstream, 4); /* bx[3:0] */
+ b[0] |= bcdec__bitstream_read_bit(&bstream) << 10; /* bw[10] */
+ b[3] |= bcdec__bitstream_read_bit(&bstream) << 1; /* bz[1] */
+ b[2] |= bcdec__bitstream_read_bits(&bstream, 4); /* by[3:0] */
+ r[2] |= bcdec__bitstream_read_bits(&bstream, 5); /* ry[4:0] */
+ b[3] |= bcdec__bitstream_read_bit(&bstream) << 2; /* bz[2] */
+ r[3] |= bcdec__bitstream_read_bits(&bstream, 5); /* rz[4:0] */
+ b[3] |= bcdec__bitstream_read_bit(&bstream) << 3; /* bz[3] */
+ partition = bcdec__bitstream_read_bits(&bstream, 5); /* d[4:0] */
+ mode = 2;
+ } break;
+
+ /* mode 4 */
+ case 0b00110: {
+ /* Partitition indices: 46 bits
+ Partition: 5 bits
+ Color Endpoints: 72 bits (11.444, 11.555, 11.444) */
+ r[0] |= bcdec__bitstream_read_bits(&bstream, 10); /* rw[9:0] */
+ g[0] |= bcdec__bitstream_read_bits(&bstream, 10); /* gw[9:0] */
+ b[0] |= bcdec__bitstream_read_bits(&bstream, 10); /* bw[9:0] */
+ r[1] |= bcdec__bitstream_read_bits(&bstream, 4); /* rx[3:0] */
+ r[0] |= bcdec__bitstream_read_bit(&bstream) << 10; /* rw[10] */
+ g[3] |= bcdec__bitstream_read_bit(&bstream) << 4; /* gz[4] */
+ g[2] |= bcdec__bitstream_read_bits(&bstream, 4); /* gy[3:0] */
+ g[1] |= bcdec__bitstream_read_bits(&bstream, 5); /* gx[4:0] */
+ g[0] |= bcdec__bitstream_read_bit(&bstream) << 10; /* gw[10] */
+ g[3] |= bcdec__bitstream_read_bits(&bstream, 4); /* gz[3:0] */
+ b[1] |= bcdec__bitstream_read_bits(&bstream, 4); /* bx[3:0] */
+ b[0] |= bcdec__bitstream_read_bit(&bstream) << 10; /* bw[10] */
+ b[3] |= bcdec__bitstream_read_bit(&bstream) << 1; /* bz[1] */
+ b[2] |= bcdec__bitstream_read_bits(&bstream, 4); /* by[3:0] */
+ r[2] |= bcdec__bitstream_read_bits(&bstream, 4); /* ry[3:0] */
+ b[3] |= bcdec__bitstream_read_bit(&bstream); /* bz[0] */
+ b[3] |= bcdec__bitstream_read_bit(&bstream) << 2; /* bz[2] */
+ r[3] |= bcdec__bitstream_read_bits(&bstream, 4); /* rz[3:0] */
+ g[2] |= bcdec__bitstream_read_bit(&bstream) << 4; /* gy[4] */
+ b[3] |= bcdec__bitstream_read_bit(&bstream) << 3; /* bz[3] */
+ partition = bcdec__bitstream_read_bits(&bstream, 5); /* d[4:0] */
+ mode = 3;
+ } break;
+
+ /* mode 5 */
+ case 0b01010: {
+ /* Partitition indices: 46 bits
+ Partition: 5 bits
+ Color Endpoints: 72 bits (11.444, 11.444, 11.555) */
+ r[0] |= bcdec__bitstream_read_bits(&bstream, 10); /* rw[9:0] */
+ g[0] |= bcdec__bitstream_read_bits(&bstream, 10); /* gw[9:0] */
+ b[0] |= bcdec__bitstream_read_bits(&bstream, 10); /* bw[9:0] */
+ r[1] |= bcdec__bitstream_read_bits(&bstream, 4); /* rx[3:0] */
+ r[0] |= bcdec__bitstream_read_bit(&bstream) << 10; /* rw[10] */
+ b[2] |= bcdec__bitstream_read_bit(&bstream) << 4; /* by[4] */
+ g[2] |= bcdec__bitstream_read_bits(&bstream, 4); /* gy[3:0] */
+ g[1] |= bcdec__bitstream_read_bits(&bstream, 4); /* gx[3:0] */
+ g[0] |= bcdec__bitstream_read_bit(&bstream) << 10; /* gw[10] */
+ b[3] |= bcdec__bitstream_read_bit(&bstream); /* bz[0] */
+ g[3] |= bcdec__bitstream_read_bits(&bstream, 4); /* gz[3:0] */
+ b[1] |= bcdec__bitstream_read_bits(&bstream, 5); /* bx[4:0] */
+ b[0] |= bcdec__bitstream_read_bit(&bstream) << 10; /* bw[10] */
+ b[2] |= bcdec__bitstream_read_bits(&bstream, 4); /* by[3:0] */
+ r[2] |= bcdec__bitstream_read_bits(&bstream, 4); /* ry[3:0] */
+ b[3] |= bcdec__bitstream_read_bit(&bstream) << 1; /* bz[1] */
+ b[3] |= bcdec__bitstream_read_bit(&bstream) << 2; /* bz[2] */
+ r[3] |= bcdec__bitstream_read_bits(&bstream, 4); /* rz[3:0] */
+ b[3] |= bcdec__bitstream_read_bit(&bstream) << 4; /* bz[4] */
+ b[3] |= bcdec__bitstream_read_bit(&bstream) << 3; /* bz[3] */
+ partition = bcdec__bitstream_read_bits(&bstream, 5); /* d[4:0] */
+ mode = 4;
+ } break;
+
+ /* mode 6 */
+ case 0b01110: {
+ /* Partitition indices: 46 bits
+ Partition: 5 bits
+ Color Endpoints: 72 bits (9555, 9555, 9555) */
+ r[0] |= bcdec__bitstream_read_bits(&bstream, 9); /* rw[8:0] */
+ b[2] |= bcdec__bitstream_read_bit(&bstream) << 4; /* by[4] */
+ g[0] |= bcdec__bitstream_read_bits(&bstream, 9); /* gw[8:0] */
+ g[2] |= bcdec__bitstream_read_bit(&bstream) << 4; /* gy[4] */
+ b[0] |= bcdec__bitstream_read_bits(&bstream, 9); /* bw[8:0] */
+ b[3] |= bcdec__bitstream_read_bit(&bstream) << 4; /* bz[4] */
+ r[1] |= bcdec__bitstream_read_bits(&bstream, 5); /* rx[4:0] */
+ g[3] |= bcdec__bitstream_read_bit(&bstream) << 4; /* gz[4] */
+ g[2] |= bcdec__bitstream_read_bits(&bstream, 4); /* gy[3:0] */
+ g[1] |= bcdec__bitstream_read_bits(&bstream, 5); /* gx[4:0] */
+ b[3] |= bcdec__bitstream_read_bit(&bstream); /* bz[0] */
+ g[3] |= bcdec__bitstream_read_bits(&bstream, 4); /* gx[3:0] */
+ b[1] |= bcdec__bitstream_read_bits(&bstream, 5); /* bx[4:0] */
+ b[3] |= bcdec__bitstream_read_bit(&bstream) << 1; /* bz[1] */
+ b[2] |= bcdec__bitstream_read_bits(&bstream, 4); /* by[3:0] */
+ r[2] |= bcdec__bitstream_read_bits(&bstream, 5); /* ry[4:0] */
+ b[3] |= bcdec__bitstream_read_bit(&bstream) << 2; /* bz[2] */
+ r[3] |= bcdec__bitstream_read_bits(&bstream, 5); /* rz[4:0] */
+ b[3] |= bcdec__bitstream_read_bit(&bstream) << 3; /* bz[3] */
+ partition = bcdec__bitstream_read_bits(&bstream, 5); /* d[4:0] */
+ mode = 5;
+ } break;
+
+ /* mode 7 */
+ case 0b10010: {
+ /* Partitition indices: 46 bits
+ Partition: 5 bits
+ Color Endpoints: 72 bits (8666, 8555, 8555) */
+ r[0] |= bcdec__bitstream_read_bits(&bstream, 8); /* rw[7:0] */
+ g[3] |= bcdec__bitstream_read_bit(&bstream) << 4; /* gz[4] */
+ b[2] |= bcdec__bitstream_read_bit(&bstream) << 4; /* by[4] */
+ g[0] |= bcdec__bitstream_read_bits(&bstream, 8); /* gw[7:0] */
+ b[3] |= bcdec__bitstream_read_bit(&bstream) << 2; /* bz[2] */
+ g[2] |= bcdec__bitstream_read_bit(&bstream) << 4; /* gy[4] */
+ b[0] |= bcdec__bitstream_read_bits(&bstream, 8); /* bw[7:0] */
+ b[3] |= bcdec__bitstream_read_bit(&bstream) << 3; /* bz[3] */
+ b[3] |= bcdec__bitstream_read_bit(&bstream) << 4; /* bz[4] */
+ r[1] |= bcdec__bitstream_read_bits(&bstream, 6); /* rx[5:0] */
+ g[2] |= bcdec__bitstream_read_bits(&bstream, 4); /* gy[3:0] */
+ g[1] |= bcdec__bitstream_read_bits(&bstream, 5); /* gx[4:0] */
+ b[3] |= bcdec__bitstream_read_bit(&bstream); /* bz[0] */
+ g[3] |= bcdec__bitstream_read_bits(&bstream, 4); /* gz[3:0] */
+ b[1] |= bcdec__bitstream_read_bits(&bstream, 5); /* bx[4:0] */
+ b[3] |= bcdec__bitstream_read_bit(&bstream) << 1; /* bz[1] */
+ b[2] |= bcdec__bitstream_read_bits(&bstream, 4); /* by[3:0] */
+ r[2] |= bcdec__bitstream_read_bits(&bstream, 6); /* ry[5:0] */
+ r[3] |= bcdec__bitstream_read_bits(&bstream, 6); /* rz[5:0] */
+ partition = bcdec__bitstream_read_bits(&bstream, 5); /* d[4:0] */
+ mode = 6;
+ } break;
+
+ /* mode 8 */
+ case 0b10110: {
+ /* Partitition indices: 46 bits
+ Partition: 5 bits
+ Color Endpoints: 72 bits (8555, 8666, 8555) */
+ r[0] |= bcdec__bitstream_read_bits(&bstream, 8); /* rw[7:0] */
+ b[3] |= bcdec__bitstream_read_bit(&bstream); /* bz[0] */
+ b[2] |= bcdec__bitstream_read_bit(&bstream) << 4; /* by[4] */
+ g[0] |= bcdec__bitstream_read_bits(&bstream, 8); /* gw[7:0] */
+ g[2] |= bcdec__bitstream_read_bit(&bstream) << 5; /* gy[5] */
+ g[2] |= bcdec__bitstream_read_bit(&bstream) << 4; /* gy[4] */
+ b[0] |= bcdec__bitstream_read_bits(&bstream, 8); /* bw[7:0] */
+ g[3] |= bcdec__bitstream_read_bit(&bstream) << 5; /* gz[5] */
+ b[3] |= bcdec__bitstream_read_bit(&bstream) << 4; /* bz[4] */
+ r[1] |= bcdec__bitstream_read_bits(&bstream, 5); /* rx[4:0] */
+ g[3] |= bcdec__bitstream_read_bit(&bstream) << 4; /* gz[4] */
+ g[2] |= bcdec__bitstream_read_bits(&bstream, 4); /* gy[3:0] */
+ g[1] |= bcdec__bitstream_read_bits(&bstream, 6); /* gx[5:0] */
+ g[3] |= bcdec__bitstream_read_bits(&bstream, 4); /* zx[3:0] */
+ b[1] |= bcdec__bitstream_read_bits(&bstream, 5); /* bx[4:0] */
+ b[3] |= bcdec__bitstream_read_bit(&bstream) << 1; /* bz[1] */
+ b[2] |= bcdec__bitstream_read_bits(&bstream, 4); /* by[3:0] */
+ r[2] |= bcdec__bitstream_read_bits(&bstream, 5); /* ry[4:0] */
+ b[3] |= bcdec__bitstream_read_bit(&bstream) << 2; /* bz[2] */
+ r[3] |= bcdec__bitstream_read_bits(&bstream, 5); /* rz[4:0] */
+ b[3] |= bcdec__bitstream_read_bit(&bstream) << 3; /* bz[3] */
+ partition = bcdec__bitstream_read_bits(&bstream, 5); /* d[4:0] */
+ mode = 7;
+ } break;
+
+ /* mode 9 */
+ case 0b11010: {
+ /* Partitition indices: 46 bits
+ Partition: 5 bits
+ Color Endpoints: 72 bits (8555, 8555, 8666) */
+ r[0] |= bcdec__bitstream_read_bits(&bstream, 8); /* rw[7:0] */
+ b[3] |= bcdec__bitstream_read_bit(&bstream) << 1; /* bz[1] */
+ b[2] |= bcdec__bitstream_read_bit(&bstream) << 4; /* by[4] */
+ g[0] |= bcdec__bitstream_read_bits(&bstream, 8); /* gw[7:0] */
+ b[2] |= bcdec__bitstream_read_bit(&bstream) << 5; /* by[5] */
+ g[2] |= bcdec__bitstream_read_bit(&bstream) << 4; /* gy[4] */
+ b[0] |= bcdec__bitstream_read_bits(&bstream, 8); /* bw[7:0] */
+ b[3] |= bcdec__bitstream_read_bit(&bstream) << 5; /* bz[5] */
+ b[3] |= bcdec__bitstream_read_bit(&bstream) << 4; /* bz[4] */
+ r[1] |= bcdec__bitstream_read_bits(&bstream, 5); /* bw[4:0] */
+ g[3] |= bcdec__bitstream_read_bit(&bstream) << 4; /* gz[4] */
+ g[2] |= bcdec__bitstream_read_bits(&bstream, 4); /* gy[3:0] */
+ g[1] |= bcdec__bitstream_read_bits(&bstream, 5); /* gx[4:0] */
+ b[3] |= bcdec__bitstream_read_bit(&bstream); /* bz[0] */
+ g[3] |= bcdec__bitstream_read_bits(&bstream, 4); /* gz[3:0] */
+ b[1] |= bcdec__bitstream_read_bits(&bstream, 6); /* bx[5:0] */
+ b[2] |= bcdec__bitstream_read_bits(&bstream, 4); /* by[3:0] */
+ r[2] |= bcdec__bitstream_read_bits(&bstream, 5); /* ry[4:0] */
+ b[3] |= bcdec__bitstream_read_bit(&bstream) << 2; /* bz[2] */
+ r[3] |= bcdec__bitstream_read_bits(&bstream, 5); /* rz[4:0] */
+ b[3] |= bcdec__bitstream_read_bit(&bstream) << 3; /* bz[3] */
+ partition = bcdec__bitstream_read_bits(&bstream, 5); /* d[4:0] */
+ mode = 8;
+ } break;
+
+ /* mode 10 */
+ case 0b11110: {
+ /* Partitition indices: 46 bits
+ Partition: 5 bits
+ Color Endpoints: 72 bits (6666, 6666, 6666) */
+ r[0] |= bcdec__bitstream_read_bits(&bstream, 6); /* rw[5:0] */
+ g[3] |= bcdec__bitstream_read_bit(&bstream) << 4; /* gz[4] */
+ b[3] |= bcdec__bitstream_read_bit(&bstream); /* bz[0] */
+ b[3] |= bcdec__bitstream_read_bit(&bstream) << 1; /* bz[1] */
+ b[2] |= bcdec__bitstream_read_bit(&bstream) << 4; /* by[4] */
+ g[0] |= bcdec__bitstream_read_bits(&bstream, 6); /* gw[5:0] */
+ g[2] |= bcdec__bitstream_read_bit(&bstream) << 5; /* gy[5] */
+ b[2] |= bcdec__bitstream_read_bit(&bstream) << 5; /* by[5] */
+ b[3] |= bcdec__bitstream_read_bit(&bstream) << 2; /* bz[2] */
+ g[2] |= bcdec__bitstream_read_bit(&bstream) << 4; /* gy[4] */
+ b[0] |= bcdec__bitstream_read_bits(&bstream, 6); /* bw[5:0] */
+ g[3] |= bcdec__bitstream_read_bit(&bstream) << 5; /* gz[5] */
+ b[3] |= bcdec__bitstream_read_bit(&bstream) << 3; /* bz[3] */
+ b[3] |= bcdec__bitstream_read_bit(&bstream) << 5; /* bz[5] */
+ b[3] |= bcdec__bitstream_read_bit(&bstream) << 4; /* bz[4] */
+ r[1] |= bcdec__bitstream_read_bits(&bstream, 6); /* rx[5:0] */
+ g[2] |= bcdec__bitstream_read_bits(&bstream, 4); /* gy[3:0] */
+ g[1] |= bcdec__bitstream_read_bits(&bstream, 6); /* gx[5:0] */
+ g[3] |= bcdec__bitstream_read_bits(&bstream, 4); /* gz[3:0] */
+ b[1] |= bcdec__bitstream_read_bits(&bstream, 6); /* bx[5:0] */
+ b[2] |= bcdec__bitstream_read_bits(&bstream, 4); /* by[3:0] */
+ r[2] |= bcdec__bitstream_read_bits(&bstream, 6); /* ry[5:0] */
+ r[3] |= bcdec__bitstream_read_bits(&bstream, 6); /* rz[5:0] */
+ partition = bcdec__bitstream_read_bits(&bstream, 5); /* d[4:0] */
+ mode = 9;
+ } break;
+
+ /* mode 11 */
+ case 0b00011: {
+ /* Partitition indices: 63 bits
+ Partition: 0 bits
+ Color Endpoints: 60 bits (10.10, 10.10, 10.10) */
+ r[0] |= bcdec__bitstream_read_bits(&bstream, 10); /* rw[9:0] */
+ g[0] |= bcdec__bitstream_read_bits(&bstream, 10); /* gw[9:0] */
+ b[0] |= bcdec__bitstream_read_bits(&bstream, 10); /* bw[9:0] */
+ r[1] |= bcdec__bitstream_read_bits(&bstream, 10); /* rx[9:0] */
+ g[1] |= bcdec__bitstream_read_bits(&bstream, 10); /* gx[9:0] */
+ b[1] |= bcdec__bitstream_read_bits(&bstream, 10); /* bx[9:0] */
+ mode = 10;
+ } break;
+
+ /* mode 12 */
+ case 0b00111: {
+ /* Partitition indices: 63 bits
+ Partition: 0 bits
+ Color Endpoints: 60 bits (11.9, 11.9, 11.9) */
+ r[0] |= bcdec__bitstream_read_bits(&bstream, 10); /* rw[9:0] */
+ g[0] |= bcdec__bitstream_read_bits(&bstream, 10); /* gw[9:0] */
+ b[0] |= bcdec__bitstream_read_bits(&bstream, 10); /* bw[9:0] */
+ r[1] |= bcdec__bitstream_read_bits(&bstream, 9); /* rx[8:0] */
+ r[0] |= bcdec__bitstream_read_bit(&bstream) << 10; /* rw[10] */
+ g[1] |= bcdec__bitstream_read_bits(&bstream, 9); /* gx[8:0] */
+ g[0] |= bcdec__bitstream_read_bit(&bstream) << 10; /* gw[10] */
+ b[1] |= bcdec__bitstream_read_bits(&bstream, 9); /* bx[8:0] */
+ b[0] |= bcdec__bitstream_read_bit(&bstream) << 10; /* bw[10] */
+ mode = 11;
+ } break;
+
+ /* mode 13 */
+ case 0b01011: {
+ /* Partitition indices: 63 bits
+ Partition: 0 bits
+ Color Endpoints: 60 bits (12.8, 12.8, 12.8) */
+ r[0] |= bcdec__bitstream_read_bits(&bstream, 10); /* rw[9:0] */
+ g[0] |= bcdec__bitstream_read_bits(&bstream, 10); /* gw[9:0] */
+ b[0] |= bcdec__bitstream_read_bits(&bstream, 10); /* bw[9:0] */
+ r[1] |= bcdec__bitstream_read_bits(&bstream, 8); /* rx[7:0] */
+ r[0] |= bcdec__bitstream_read_bits_r(&bstream, 2) << 10;/* rx[10:11] */
+ g[1] |= bcdec__bitstream_read_bits(&bstream, 8); /* gx[7:0] */
+ g[0] |= bcdec__bitstream_read_bits_r(&bstream, 2) << 10;/* gx[10:11] */
+ b[1] |= bcdec__bitstream_read_bits(&bstream, 8); /* bx[7:0] */
+ b[0] |= bcdec__bitstream_read_bits_r(&bstream, 2) << 10;/* bx[10:11] */
+ mode = 12;
+ } break;
+
+ /* mode 14 */
+ case 0b01111: {
+ /* Partitition indices: 63 bits
+ Partition: 0 bits
+ Color Endpoints: 60 bits (16.4, 16.4, 16.4) */
+ r[0] |= bcdec__bitstream_read_bits(&bstream, 10); /* rw[9:0] */
+ g[0] |= bcdec__bitstream_read_bits(&bstream, 10); /* gw[9:0] */
+ b[0] |= bcdec__bitstream_read_bits(&bstream, 10); /* bw[9:0] */
+ r[1] |= bcdec__bitstream_read_bits(&bstream, 4); /* rx[3:0] */
+ r[0] |= bcdec__bitstream_read_bits_r(&bstream, 6) << 10;/* rw[10:15] */
+ g[1] |= bcdec__bitstream_read_bits(&bstream, 4); /* gx[3:0] */
+ g[0] |= bcdec__bitstream_read_bits_r(&bstream, 6) << 10;/* gw[10:15] */
+ b[1] |= bcdec__bitstream_read_bits(&bstream, 4); /* bx[3:0] */
+ b[0] |= bcdec__bitstream_read_bits_r(&bstream, 6) << 10;/* bw[10:15] */
+ mode = 13;
+ } break;
+
+ default: {
+ /* Modes 10011, 10111, 11011, and 11111 (not shown) are reserved.
+ Do not use these in your encoder. If the hardware is passed blocks
+ with one of these modes specified, the resulting decompressed block
+ must contain all zeroes in all channels except for the alpha channel. */
+ for (i = 0; i < 4; ++i) {
+ for (j = 0; j < 4; ++j) {
+ decompressed[j * 3 + 0] = 0;
+ decompressed[j * 3 + 1] = 0;
+ decompressed[j * 3 + 2] = 0;
+ }
+ decompressed += destinationPitch;
+ }
+
+ return;
+ }
+ }
+
+ numPartitions = (mode >= 10) ? 0 : 1;
+
+ actualBits0Mode = actual_bits_count[0][mode];
+ if (isSigned) {
+ r[0] = bcdec__extend_sign(r[0], actualBits0Mode);
+ g[0] = bcdec__extend_sign(g[0], actualBits0Mode);
+ b[0] = bcdec__extend_sign(b[0], actualBits0Mode);
+ }
+
+ /* Mode 11 (like Mode 10) does not use delta compression,
+ and instead stores both color endpoints explicitly. */
+ if ((mode != 9 && mode != 10) || isSigned) {
+ for (i = 1; i < (numPartitions + 1) * 2; ++i) {
+ r[i] = bcdec__extend_sign(r[i], actual_bits_count[1][mode]);
+ g[i] = bcdec__extend_sign(g[i], actual_bits_count[2][mode]);
+ b[i] = bcdec__extend_sign(b[i], actual_bits_count[3][mode]);
+ }
+ }
+
+ if (mode != 9 && mode != 10) {
+ for (i = 1; i < (numPartitions + 1) * 2; ++i) {
+ r[i] = bcdec__transform_inverse(r[i], r[0], actualBits0Mode, isSigned);
+ g[i] = bcdec__transform_inverse(g[i], g[0], actualBits0Mode, isSigned);
+ b[i] = bcdec__transform_inverse(b[i], b[0], actualBits0Mode, isSigned);
+ }
+ }
+
+ for (i = 0; i < (numPartitions + 1) * 2; ++i) {
+ r[i] = bcdec__unquantize(r[i], actualBits0Mode, isSigned);
+ g[i] = bcdec__unquantize(g[i], actualBits0Mode, isSigned);
+ b[i] = bcdec__unquantize(b[i], actualBits0Mode, isSigned);
+ }
+
+ weights = (mode >= 10) ? aWeight4 : aWeight3;
+ for (i = 0; i < 4; ++i) {
+ for (j = 0; j < 4; ++j) {
+ partitionSet = (mode >= 10) ? ((i|j) ? 0 : 128) : partition_sets[partition][i][j];
+
+ indexBits = (mode >= 10) ? 4 : 3;
+ /* fix-up index is specified with one less bit */
+ /* The fix-up index for subset 0 is always index 0 */
+ if (partitionSet & 0x80) {
+ indexBits--;
+ }
+ partitionSet &= 0x01;
+
+ index = bcdec__bitstream_read_bits(&bstream, indexBits);
+
+ ep_i = partitionSet * 2;
+ decompressed[j * 3 + 0] = bcdec__finish_unquantize(
+ bcdec__interpolate(r[ep_i], r[ep_i+1], weights, index), isSigned);
+ decompressed[j * 3 + 1] = bcdec__finish_unquantize(
+ bcdec__interpolate(g[ep_i], g[ep_i+1], weights, index), isSigned);
+ decompressed[j * 3 + 2] = bcdec__finish_unquantize(
+ bcdec__interpolate(b[ep_i], b[ep_i+1], weights, index), isSigned);
+ }
+
+ decompressed += destinationPitch;
+ }
+}
+
+BCDECDEF void bcdec_bc6h_float(const void* compressedBlock, void* decompressedBlock, int destinationPitch, int isSigned) {
+ unsigned short block[16*3];
+ float* decompressed;
+ const unsigned short* b;
+ int i, j;
+
+ bcdec_bc6h_half(compressedBlock, block, 4*3, isSigned);
+ b = block;
+ decompressed = (float*)decompressedBlock;
+ for (i = 0; i < 4; ++i) {
+ for (j = 0; j < 4; ++j) {
+ decompressed[j * 3 + 0] = bcdec__half_to_float_quick(*b++);
+ decompressed[j * 3 + 1] = bcdec__half_to_float_quick(*b++);
+ decompressed[j * 3 + 2] = bcdec__half_to_float_quick(*b++);
+ }
+ decompressed += destinationPitch;
+ }
+}
+
+static void bcdec__swap_values(int* a, int* b) {
+ a[0] ^= b[0], b[0] ^= a[0], a[0] ^= b[0];
+}
+
+BCDECDEF void bcdec_bc7(const void* compressedBlock, void* decompressedBlock, int destinationPitch) {
+ static char actual_bits_count[2][8] = {
+ { 4, 6, 5, 7, 5, 7, 7, 5 }, /* RGBA */
+ { 0, 0, 0, 0, 6, 8, 7, 5 }, /* Alpha */
+ };
+
+ /* There are 64 possible partition sets for a two-region tile.
+ Each 4x4 block represents a single shape.
+ Here also every fix-up index has MSB bit set. */
+ static unsigned char partition_sets[2][64][4][4] = {
+ { /* Partition table for 2-subset BPTC */
+ { {128, 0, 1, 1}, {0, 0, 1, 1}, { 0, 0, 1, 1}, {0, 0, 1, 129} }, /* 0 */
+ { {128, 0, 0, 1}, {0, 0, 0, 1}, { 0, 0, 0, 1}, {0, 0, 0, 129} }, /* 1 */
+ { {128, 1, 1, 1}, {0, 1, 1, 1}, { 0, 1, 1, 1}, {0, 1, 1, 129} }, /* 2 */
+ { {128, 0, 0, 1}, {0, 0, 1, 1}, { 0, 0, 1, 1}, {0, 1, 1, 129} }, /* 3 */
+ { {128, 0, 0, 0}, {0, 0, 0, 1}, { 0, 0, 0, 1}, {0, 0, 1, 129} }, /* 4 */
+ { {128, 0, 1, 1}, {0, 1, 1, 1}, { 0, 1, 1, 1}, {1, 1, 1, 129} }, /* 5 */
+ { {128, 0, 0, 1}, {0, 0, 1, 1}, { 0, 1, 1, 1}, {1, 1, 1, 129} }, /* 6 */
+ { {128, 0, 0, 0}, {0, 0, 0, 1}, { 0, 0, 1, 1}, {0, 1, 1, 129} }, /* 7 */
+ { {128, 0, 0, 0}, {0, 0, 0, 0}, { 0, 0, 0, 1}, {0, 0, 1, 129} }, /* 8 */
+ { {128, 0, 1, 1}, {0, 1, 1, 1}, { 1, 1, 1, 1}, {1, 1, 1, 129} }, /* 9 */
+ { {128, 0, 0, 0}, {0, 0, 0, 1}, { 0, 1, 1, 1}, {1, 1, 1, 129} }, /* 10 */
+ { {128, 0, 0, 0}, {0, 0, 0, 0}, { 0, 0, 0, 1}, {0, 1, 1, 129} }, /* 11 */
+ { {128, 0, 0, 1}, {0, 1, 1, 1}, { 1, 1, 1, 1}, {1, 1, 1, 129} }, /* 12 */
+ { {128, 0, 0, 0}, {0, 0, 0, 0}, { 1, 1, 1, 1}, {1, 1, 1, 129} }, /* 13 */
+ { {128, 0, 0, 0}, {1, 1, 1, 1}, { 1, 1, 1, 1}, {1, 1, 1, 129} }, /* 14 */
+ { {128, 0, 0, 0}, {0, 0, 0, 0}, { 0, 0, 0, 0}, {1, 1, 1, 129} }, /* 15 */
+ { {128, 0, 0, 0}, {1, 0, 0, 0}, { 1, 1, 1, 0}, {1, 1, 1, 129} }, /* 16 */
+ { {128, 1, 129, 1}, {0, 0, 0, 1}, { 0, 0, 0, 0}, {0, 0, 0, 0} }, /* 17 */
+ { {128, 0, 0, 0}, {0, 0, 0, 0}, {129, 0, 0, 0}, {1, 1, 1, 0} }, /* 18 */
+ { {128, 1, 129, 1}, {0, 0, 1, 1}, { 0, 0, 0, 1}, {0, 0, 0, 0} }, /* 19 */
+ { {128, 0, 129, 1}, {0, 0, 0, 1}, { 0, 0, 0, 0}, {0, 0, 0, 0} }, /* 20 */
+ { {128, 0, 0, 0}, {1, 0, 0, 0}, {129, 1, 0, 0}, {1, 1, 1, 0} }, /* 21 */
+ { {128, 0, 0, 0}, {0, 0, 0, 0}, {129, 0, 0, 0}, {1, 1, 0, 0} }, /* 22 */
+ { {128, 1, 1, 1}, {0, 0, 1, 1}, { 0, 0, 1, 1}, {0, 0, 0, 129} }, /* 23 */
+ { {128, 0, 129, 1}, {0, 0, 0, 1}, { 0, 0, 0, 1}, {0, 0, 0, 0} }, /* 24 */
+ { {128, 0, 0, 0}, {1, 0, 0, 0}, {129, 0, 0, 0}, {1, 1, 0, 0} }, /* 25 */
+ { {128, 1, 129, 0}, {0, 1, 1, 0}, { 0, 1, 1, 0}, {0, 1, 1, 0} }, /* 26 */
+ { {128, 0, 129, 1}, {0, 1, 1, 0}, { 0, 1, 1, 0}, {1, 1, 0, 0} }, /* 27 */
+ { {128, 0, 0, 1}, {0, 1, 1, 1}, {129, 1, 1, 0}, {1, 0, 0, 0} }, /* 28 */
+ { {128, 0, 0, 0}, {1, 1, 1, 1}, {129, 1, 1, 1}, {0, 0, 0, 0} }, /* 29 */
+ { {128, 1, 129, 1}, {0, 0, 0, 1}, { 1, 0, 0, 0}, {1, 1, 1, 0} }, /* 30 */
+ { {128, 0, 129, 1}, {1, 0, 0, 1}, { 1, 0, 0, 1}, {1, 1, 0, 0} }, /* 31 */
+ { {128, 1, 0, 1}, {0, 1, 0, 1}, { 0, 1, 0, 1}, {0, 1, 0, 129} }, /* 32 */
+ { {128, 0, 0, 0}, {1, 1, 1, 1}, { 0, 0, 0, 0}, {1, 1, 1, 129} }, /* 33 */
+ { {128, 1, 0, 1}, {1, 0, 129, 0}, { 0, 1, 0, 1}, {1, 0, 1, 0} }, /* 34 */
+ { {128, 0, 1, 1}, {0, 0, 1, 1}, {129, 1, 0, 0}, {1, 1, 0, 0} }, /* 35 */
+ { {128, 0, 129, 1}, {1, 1, 0, 0}, { 0, 0, 1, 1}, {1, 1, 0, 0} }, /* 36 */
+ { {128, 1, 0, 1}, {0, 1, 0, 1}, {129, 0, 1, 0}, {1, 0, 1, 0} }, /* 37 */
+ { {128, 1, 1, 0}, {1, 0, 0, 1}, { 0, 1, 1, 0}, {1, 0, 0, 129} }, /* 38 */
+ { {128, 1, 0, 1}, {1, 0, 1, 0}, { 1, 0, 1, 0}, {0, 1, 0, 129} }, /* 39 */
+ { {128, 1, 129, 1}, {0, 0, 1, 1}, { 1, 1, 0, 0}, {1, 1, 1, 0} }, /* 40 */
+ { {128, 0, 0, 1}, {0, 0, 1, 1}, {129, 1, 0, 0}, {1, 0, 0, 0} }, /* 41 */
+ { {128, 0, 129, 1}, {0, 0, 1, 0}, { 0, 1, 0, 0}, {1, 1, 0, 0} }, /* 42 */
+ { {128, 0, 129, 1}, {1, 0, 1, 1}, { 1, 1, 0, 1}, {1, 1, 0, 0} }, /* 43 */
+ { {128, 1, 129, 0}, {1, 0, 0, 1}, { 1, 0, 0, 1}, {0, 1, 1, 0} }, /* 44 */
+ { {128, 0, 1, 1}, {1, 1, 0, 0}, { 1, 1, 0, 0}, {0, 0, 1, 129} }, /* 45 */
+ { {128, 1, 1, 0}, {0, 1, 1, 0}, { 1, 0, 0, 1}, {1, 0, 0, 129} }, /* 46 */
+ { {128, 0, 0, 0}, {0, 1, 129, 0}, { 0, 1, 1, 0}, {0, 0, 0, 0} }, /* 47 */
+ { {128, 1, 0, 0}, {1, 1, 129, 0}, { 0, 1, 0, 0}, {0, 0, 0, 0} }, /* 48 */
+ { {128, 0, 129, 0}, {0, 1, 1, 1}, { 0, 0, 1, 0}, {0, 0, 0, 0} }, /* 49 */
+ { {128, 0, 0, 0}, {0, 0, 129, 0}, { 0, 1, 1, 1}, {0, 0, 1, 0} }, /* 50 */
+ { {128, 0, 0, 0}, {0, 1, 0, 0}, {129, 1, 1, 0}, {0, 1, 0, 0} }, /* 51 */
+ { {128, 1, 1, 0}, {1, 1, 0, 0}, { 1, 0, 0, 1}, {0, 0, 1, 129} }, /* 52 */
+ { {128, 0, 1, 1}, {0, 1, 1, 0}, { 1, 1, 0, 0}, {1, 0, 0, 129} }, /* 53 */
+ { {128, 1, 129, 0}, {0, 0, 1, 1}, { 1, 0, 0, 1}, {1, 1, 0, 0} }, /* 54 */
+ { {128, 0, 129, 1}, {1, 0, 0, 1}, { 1, 1, 0, 0}, {0, 1, 1, 0} }, /* 55 */
+ { {128, 1, 1, 0}, {1, 1, 0, 0}, { 1, 1, 0, 0}, {1, 0, 0, 129} }, /* 56 */
+ { {128, 1, 1, 0}, {0, 0, 1, 1}, { 0, 0, 1, 1}, {1, 0, 0, 129} }, /* 57 */
+ { {128, 1, 1, 1}, {1, 1, 1, 0}, { 1, 0, 0, 0}, {0, 0, 0, 129} }, /* 58 */
+ { {128, 0, 0, 1}, {1, 0, 0, 0}, { 1, 1, 1, 0}, {0, 1, 1, 129} }, /* 59 */
+ { {128, 0, 0, 0}, {1, 1, 1, 1}, { 0, 0, 1, 1}, {0, 0, 1, 129} }, /* 60 */
+ { {128, 0, 129, 1}, {0, 0, 1, 1}, { 1, 1, 1, 1}, {0, 0, 0, 0} }, /* 61 */
+ { {128, 0, 129, 0}, {0, 0, 1, 0}, { 1, 1, 1, 0}, {1, 1, 1, 0} }, /* 62 */
+ { {128, 1, 0, 0}, {0, 1, 0, 0}, { 0, 1, 1, 1}, {0, 1, 1, 129} } /* 63 */
+ },
+ { /* Partition table for 3-subset BPTC */
+ { {128, 0, 1, 129}, {0, 0, 1, 1}, { 0, 2, 2, 1}, { 2, 2, 2, 130} }, /* 0 */
+ { {128, 0, 0, 129}, {0, 0, 1, 1}, {130, 2, 1, 1}, { 2, 2, 2, 1} }, /* 1 */
+ { {128, 0, 0, 0}, {2, 0, 0, 1}, {130, 2, 1, 1}, { 2, 2, 1, 129} }, /* 2 */
+ { {128, 2, 2, 130}, {0, 0, 2, 2}, { 0, 0, 1, 1}, { 0, 1, 1, 129} }, /* 3 */
+ { {128, 0, 0, 0}, {0, 0, 0, 0}, {129, 1, 2, 2}, { 1, 1, 2, 130} }, /* 4 */
+ { {128, 0, 1, 129}, {0, 0, 1, 1}, { 0, 0, 2, 2}, { 0, 0, 2, 130} }, /* 5 */
+ { {128, 0, 2, 130}, {0, 0, 2, 2}, { 1, 1, 1, 1}, { 1, 1, 1, 129} }, /* 6 */
+ { {128, 0, 1, 1}, {0, 0, 1, 1}, {130, 2, 1, 1}, { 2, 2, 1, 129} }, /* 7 */
+ { {128, 0, 0, 0}, {0, 0, 0, 0}, {129, 1, 1, 1}, { 2, 2, 2, 130} }, /* 8 */
+ { {128, 0, 0, 0}, {1, 1, 1, 1}, {129, 1, 1, 1}, { 2, 2, 2, 130} }, /* 9 */
+ { {128, 0, 0, 0}, {1, 1, 129, 1}, { 2, 2, 2, 2}, { 2, 2, 2, 130} }, /* 10 */
+ { {128, 0, 1, 2}, {0, 0, 129, 2}, { 0, 0, 1, 2}, { 0, 0, 1, 130} }, /* 11 */
+ { {128, 1, 1, 2}, {0, 1, 129, 2}, { 0, 1, 1, 2}, { 0, 1, 1, 130} }, /* 12 */
+ { {128, 1, 2, 2}, {0, 129, 2, 2}, { 0, 1, 2, 2}, { 0, 1, 2, 130} }, /* 13 */
+ { {128, 0, 1, 129}, {0, 1, 1, 2}, { 1, 1, 2, 2}, { 1, 2, 2, 130} }, /* 14 */
+ { {128, 0, 1, 129}, {2, 0, 0, 1}, {130, 2, 0, 0}, { 2, 2, 2, 0} }, /* 15 */
+ { {128, 0, 0, 129}, {0, 0, 1, 1}, { 0, 1, 1, 2}, { 1, 1, 2, 130} }, /* 16 */
+ { {128, 1, 1, 129}, {0, 0, 1, 1}, {130, 0, 0, 1}, { 2, 2, 0, 0} }, /* 17 */
+ { {128, 0, 0, 0}, {1, 1, 2, 2}, {129, 1, 2, 2}, { 1, 1, 2, 130} }, /* 18 */
+ { {128, 0, 2, 130}, {0, 0, 2, 2}, { 0, 0, 2, 2}, { 1, 1, 1, 129} }, /* 19 */
+ { {128, 1, 1, 129}, {0, 1, 1, 1}, { 0, 2, 2, 2}, { 0, 2, 2, 130} }, /* 20 */
+ { {128, 0, 0, 129}, {0, 0, 0, 1}, {130, 2, 2, 1}, { 2, 2, 2, 1} }, /* 21 */
+ { {128, 0, 0, 0}, {0, 0, 129, 1}, { 0, 1, 2, 2}, { 0, 1, 2, 130} }, /* 22 */
+ { {128, 0, 0, 0}, {1, 1, 0, 0}, {130, 2, 129, 0}, { 2, 2, 1, 0} }, /* 23 */
+ { {128, 1, 2, 130}, {0, 129, 2, 2}, { 0, 0, 1, 1}, { 0, 0, 0, 0} }, /* 24 */
+ { {128, 0, 1, 2}, {0, 0, 1, 2}, {129, 1, 2, 2}, { 2, 2, 2, 130} }, /* 25 */
+ { {128, 1, 1, 0}, {1, 2, 130, 1}, {129, 2, 2, 1}, { 0, 1, 1, 0} }, /* 26 */
+ { {128, 0, 0, 0}, {0, 1, 129, 0}, { 1, 2, 130, 1}, { 1, 2, 2, 1} }, /* 27 */
+ { {128, 0, 2, 2}, {1, 1, 0, 2}, {129, 1, 0, 2}, { 0, 0, 2, 130} }, /* 28 */
+ { {128, 1, 1, 0}, {0, 129, 1, 0}, { 2, 0, 0, 2}, { 2, 2, 2, 130} }, /* 29 */
+ { {128, 0, 1, 1}, {0, 1, 2, 2}, { 0, 1, 130, 2}, { 0, 0, 1, 129} }, /* 30 */
+ { {128, 0, 0, 0}, {2, 0, 0, 0}, {130, 2, 1, 1}, { 2, 2, 2, 129} }, /* 31 */
+ { {128, 0, 0, 0}, {0, 0, 0, 2}, {129, 1, 2, 2}, { 1, 2, 2, 130} }, /* 32 */
+ { {128, 2, 2, 130}, {0, 0, 2, 2}, { 0, 0, 1, 2}, { 0, 0, 1, 129} }, /* 33 */
+ { {128, 0, 1, 129}, {0, 0, 1, 2}, { 0, 0, 2, 2}, { 0, 2, 2, 130} }, /* 34 */
+ { {128, 1, 2, 0}, {0, 129, 2, 0}, { 0, 1, 130, 0}, { 0, 1, 2, 0} }, /* 35 */
+ { {128, 0, 0, 0}, {1, 1, 129, 1}, { 2, 2, 130, 2}, { 0, 0, 0, 0} }, /* 36 */
+ { {128, 1, 2, 0}, {1, 2, 0, 1}, {130, 0, 129, 2}, { 0, 1, 2, 0} }, /* 37 */
+ { {128, 1, 2, 0}, {2, 0, 1, 2}, {129, 130, 0, 1}, { 0, 1, 2, 0} }, /* 38 */
+ { {128, 0, 1, 1}, {2, 2, 0, 0}, { 1, 1, 130, 2}, { 0, 0, 1, 129} }, /* 39 */
+ { {128, 0, 1, 1}, {1, 1, 130, 2}, { 2, 2, 0, 0}, { 0, 0, 1, 129} }, /* 40 */
+ { {128, 1, 0, 129}, {0, 1, 0, 1}, { 2, 2, 2, 2}, { 2, 2, 2, 130} }, /* 41 */
+ { {128, 0, 0, 0}, {0, 0, 0, 0}, {130, 1, 2, 1}, { 2, 1, 2, 129} }, /* 42 */
+ { {128, 0, 2, 2}, {1, 129, 2, 2}, { 0, 0, 2, 2}, { 1, 1, 2, 130} }, /* 43 */
+ { {128, 0, 2, 130}, {0, 0, 1, 1}, { 0, 0, 2, 2}, { 0, 0, 1, 129} }, /* 44 */
+ { {128, 2, 2, 0}, {1, 2, 130, 1}, { 0, 2, 2, 0}, { 1, 2, 2, 129} }, /* 45 */
+ { {128, 1, 0, 1}, {2, 2, 130, 2}, { 2, 2, 2, 2}, { 0, 1, 0, 129} }, /* 46 */
+ { {128, 0, 0, 0}, {2, 1, 2, 1}, {130, 1, 2, 1}, { 2, 1, 2, 129} }, /* 47 */
+ { {128, 1, 0, 129}, {0, 1, 0, 1}, { 0, 1, 0, 1}, { 2, 2, 2, 130} }, /* 48 */
+ { {128, 2, 2, 130}, {0, 1, 1, 1}, { 0, 2, 2, 2}, { 0, 1, 1, 129} }, /* 49 */
+ { {128, 0, 0, 2}, {1, 129, 1, 2}, { 0, 0, 0, 2}, { 1, 1, 1, 130} }, /* 50 */
+ { {128, 0, 0, 0}, {2, 129, 1, 2}, { 2, 1, 1, 2}, { 2, 1, 1, 130} }, /* 51 */
+ { {128, 2, 2, 2}, {0, 129, 1, 1}, { 0, 1, 1, 1}, { 0, 2, 2, 130} }, /* 52 */
+ { {128, 0, 0, 2}, {1, 1, 1, 2}, {129, 1, 1, 2}, { 0, 0, 0, 130} }, /* 53 */
+ { {128, 1, 1, 0}, {0, 129, 1, 0}, { 0, 1, 1, 0}, { 2, 2, 2, 130} }, /* 54 */
+ { {128, 0, 0, 0}, {0, 0, 0, 0}, { 2, 1, 129, 2}, { 2, 1, 1, 130} }, /* 55 */
+ { {128, 1, 1, 0}, {0, 129, 1, 0}, { 2, 2, 2, 2}, { 2, 2, 2, 130} }, /* 56 */
+ { {128, 0, 2, 2}, {0, 0, 1, 1}, { 0, 0, 129, 1}, { 0, 0, 2, 130} }, /* 57 */
+ { {128, 0, 2, 2}, {1, 1, 2, 2}, {129, 1, 2, 2}, { 0, 0, 2, 130} }, /* 58 */
+ { {128, 0, 0, 0}, {0, 0, 0, 0}, { 0, 0, 0, 0}, { 2, 129, 1, 130} }, /* 59 */
+ { {128, 0, 0, 130}, {0, 0, 0, 1}, { 0, 0, 0, 2}, { 0, 0, 0, 129} }, /* 60 */
+ { {128, 2, 2, 2}, {1, 2, 2, 2}, { 0, 2, 2, 2}, {129, 2, 2, 130} }, /* 61 */
+ { {128, 1, 0, 129}, {2, 2, 2, 2}, { 2, 2, 2, 2}, { 2, 2, 2, 130} }, /* 62 */
+ { {128, 1, 1, 129}, {2, 0, 1, 1}, {130, 2, 0, 1}, { 2, 2, 2, 0} } /* 63 */
+ }
+ };
+
+ static int aWeight2[] = { 0, 21, 43, 64 };
+ static int aWeight3[] = { 0, 9, 18, 27, 37, 46, 55, 64 };
+ static int aWeight4[] = { 0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 60, 64 };
+
+ static unsigned char sModeHasPBits = 0b11001011;
+
+ bcdec__bitstream_t bstream;
+ int mode, partition, numPartitions, numEndpoints, i, j, k, rotation, partitionSet;
+ int indexSelectionBit, indexBits, indexBits2, index, index2;
+ int endpoints[6][4];
+ char indices[4][4];
+ int r, g, b, a;
+ int* weights, * weights2;
+ unsigned char* decompressed;
+
+ decompressed = (unsigned char*)decompressedBlock;
+
+ bstream.low = ((unsigned long long*)compressedBlock)[0];
+ bstream.high = ((unsigned long long*)compressedBlock)[1];
+
+ for (mode = 0; mode < 8 && (0 == bcdec__bitstream_read_bit(&bstream)); ++mode);
+
+ /* unexpected mode, clear the block (transparent black) */
+ if (mode >= 8) {
+ for (i = 0; i < 4; ++i) {
+ for (j = 0; j < 4; ++j) {
+ decompressed[j * 4 + 0] = 0;
+ decompressed[j * 4 + 1] = 0;
+ decompressed[j * 4 + 2] = 0;
+ decompressed[j * 4 + 3] = 0;
+ }
+ decompressed += destinationPitch;
+ }
+
+ return;
+ }
+
+ partition = 0;
+ numPartitions = 1;
+ rotation = 0;
+ indexSelectionBit = 0;
+
+ if (mode == 0 || mode == 1 || mode == 2 || mode == 3 || mode == 7) {
+ numPartitions = (mode == 0 || mode == 2) ? 3 : 2;
+ partition = bcdec__bitstream_read_bits(&bstream, (mode == 0) ? 4 : 6);
+ }
+
+ numEndpoints = numPartitions * 2;
+
+ if (mode == 4 || mode == 5) {
+ rotation = bcdec__bitstream_read_bits(&bstream, 2);
+
+ if (mode == 4) {
+ indexSelectionBit = bcdec__bitstream_read_bit(&bstream);
+ }
+ }
+
+ /* Extract endpoints */
+ /* RGB */
+ for (i = 0; i < 3; ++i) {
+ for (j = 0; j < numEndpoints; ++j) {
+ endpoints[j][i] = bcdec__bitstream_read_bits(&bstream, actual_bits_count[0][mode]);
+ }
+ }
+ /* Alpha (if any) */
+ if (actual_bits_count[1][mode] > 0) {
+ for (j = 0; j < numEndpoints; ++j) {
+ endpoints[j][3] = bcdec__bitstream_read_bits(&bstream, actual_bits_count[1][mode]);
+ }
+ }
+
+ /* Fully decode endpoints */
+ /* First handle modes that have P-bits */
+ if (mode == 0 || mode == 1 || mode == 3 || mode == 6 || mode == 7) {
+ for (i = 0; i < numEndpoints; ++i) {
+ /* component-wise left-shift */
+ for (j = 0; j < 4; ++j) {
+ endpoints[i][j] <<= 1;
+ }
+ }
+
+ /* if P-bit is shared */
+ if (mode == 1) {
+ i = bcdec__bitstream_read_bit(&bstream);
+ j = bcdec__bitstream_read_bit(&bstream);
+
+ /* rgb component-wise insert pbits */
+ for (k = 0; k < 3; ++k) {
+ endpoints[0][k] |= i;
+ endpoints[1][k] |= i;
+ endpoints[2][k] |= j;
+ endpoints[3][k] |= j;
+ }
+ } else if (sModeHasPBits & (1 << mode)) {
+ /* unique P-bit per endpoint */
+ for (i = 0; i < numEndpoints; ++i) {
+ j = bcdec__bitstream_read_bit(&bstream);
+ for (k = 0; k < 4; ++k) {
+ endpoints[i][k] |= j;
+ }
+ }
+ }
+ }
+
+ for (i = 0; i < numEndpoints; ++i) {
+ /* get color components precision including pbit */
+ j = actual_bits_count[0][mode] + ((sModeHasPBits >> mode) & 1);
+
+ for (k = 0; k < 3; ++k) {
+ /* left shift endpoint components so that their MSB lies in bit 7 */
+ endpoints[i][k] = endpoints[i][k] << (8 - j);
+ /* Replicate each component's MSB into the LSBs revealed by the left-shift operation above */
+ endpoints[i][k] = endpoints[i][k] | (endpoints[i][k] >> j);
+ }
+
+ /* get alpha component precision including pbit */
+ j = actual_bits_count[1][mode] + ((sModeHasPBits >> mode) & 1);
+
+ /* left shift endpoint components so that their MSB lies in bit 7 */
+ endpoints[i][3] = endpoints[i][3] << (8 - j);
+ /* Replicate each component's MSB into the LSBs revealed by the left-shift operation above */
+ endpoints[i][3] = endpoints[i][3] | (endpoints[i][3] >> j);
+ }
+
+ /* If this mode does not explicitly define the alpha component */
+ /* set alpha equal to 1.0 */
+ if (!actual_bits_count[1][mode]) {
+ for (j = 0; j < numEndpoints; ++j) {
+ endpoints[j][3] = 0xFF;
+ }
+ }
+
+ /* Determine weights tables */
+ indexBits = (mode == 0 || mode == 1) ? 3 : ((mode == 6) ? 4 : 2);
+ indexBits2 = (mode == 4) ? 3 : ((mode == 5) ? 2 : 0);
+ weights = (indexBits == 2) ? aWeight2 : ((indexBits == 3) ? aWeight3 : aWeight4);
+ weights2 = (indexBits2 == 2) ? aWeight2 : aWeight3;
+
+ /* Quite inconvenient that indices aren't interleaved so we have to make 2 passes here */
+ /* Pass #1: collecting color indices */
+ for (i = 0; i < 4; ++i) {
+ for (j = 0; j < 4; ++j) {
+ partitionSet = (numPartitions == 1) ? ((i | j) ? 0 : 128) : partition_sets[numPartitions - 2][partition][i][j];
+
+ indexBits = (mode == 0 || mode == 1) ? 3 : ((mode == 6) ? 4 : 2);
+ /* fix-up index is specified with one less bit */
+ /* The fix-up index for subset 0 is always index 0 */
+ if (partitionSet & 0x80) {
+ indexBits--;
+ }
+
+ indices[i][j] = bcdec__bitstream_read_bits(&bstream, indexBits);
+ }
+ }
+
+ /* Pass #2: reading alpha indices (if any) and interpolating & rotating */
+ for (i = 0; i < 4; ++i) {
+ for (j = 0; j < 4; ++j) {
+ partitionSet = (numPartitions == 1) ? ((i|j) ? 0 : 128) : partition_sets[numPartitions - 2][partition][i][j];
+ partitionSet &= 0x03;
+
+ index = indices[i][j];
+
+ if (!indexBits2) {
+ r = bcdec__interpolate(endpoints[partitionSet * 2][0], endpoints[partitionSet * 2 + 1][0], weights, index);
+ g = bcdec__interpolate(endpoints[partitionSet * 2][1], endpoints[partitionSet * 2 + 1][1], weights, index);
+ b = bcdec__interpolate(endpoints[partitionSet * 2][2], endpoints[partitionSet * 2 + 1][2], weights, index);
+ a = bcdec__interpolate(endpoints[partitionSet * 2][3], endpoints[partitionSet * 2 + 1][3], weights, index);
+ } else {
+ index2 = bcdec__bitstream_read_bits(&bstream, (i|j) ? indexBits2 : (indexBits2 - 1));
+ /* The index value for interpolating color comes from the secondary index bits for the texel
+ if the mode has an index selection bit and its value is one, and from the primary index bits otherwise.
+ The alpha index comes from the secondary index bits if the block has a secondary index and
+ the block either doesn’t have an index selection bit or that bit is zero, and from the primary index bits otherwise. */
+ if (!indexSelectionBit) {
+ r = bcdec__interpolate(endpoints[partitionSet * 2][0], endpoints[partitionSet * 2 + 1][0], weights, index);
+ g = bcdec__interpolate(endpoints[partitionSet * 2][1], endpoints[partitionSet * 2 + 1][1], weights, index);
+ b = bcdec__interpolate(endpoints[partitionSet * 2][2], endpoints[partitionSet * 2 + 1][2], weights, index);
+ a = bcdec__interpolate(endpoints[partitionSet * 2][3], endpoints[partitionSet * 2 + 1][3], weights2, index2);
+ } else {
+ r = bcdec__interpolate(endpoints[partitionSet * 2][0], endpoints[partitionSet * 2 + 1][0], weights2, index2);
+ g = bcdec__interpolate(endpoints[partitionSet * 2][1], endpoints[partitionSet * 2 + 1][1], weights2, index2);
+ b = bcdec__interpolate(endpoints[partitionSet * 2][2], endpoints[partitionSet * 2 + 1][2], weights2, index2);
+ a = bcdec__interpolate(endpoints[partitionSet * 2][3], endpoints[partitionSet * 2 + 1][3], weights, index);
+ }
+ }
+
+ switch (rotation) {
+ case 1: { /* 01 – Block format is Scalar(R) Vector(AGB) - swap A and R */
+ bcdec__swap_values(&a, &r);
+ } break;
+ case 2: { /* 10 – Block format is Scalar(G) Vector(RAB) - swap A and G */
+ bcdec__swap_values(&a, &g);
+ } break;
+ case 3: { /* 11 - Block format is Scalar(B) Vector(RGA) - swap A and B */
+ bcdec__swap_values(&a, &b);
+ } break;
+ }
+
+ decompressed[j * 4 + 0] = r;
+ decompressed[j * 4 + 1] = g;
+ decompressed[j * 4 + 2] = b;
+ decompressed[j * 4 + 3] = a;
+ }
+
+ decompressed += destinationPitch;
+ }
+}
+
+#endif /* BCDEC_IMPLEMENTATION */
+
+#endif /* BCDEC_HEADER_INCLUDED */
+
+/* LICENSE:
+
+This software is available under 2 licenses -- choose whichever you prefer.
+
+------------------------------------------------------------------------------
+ALTERNATIVE A - MIT License
+
+Copyright (c) 2022 Sergii Kudlai
+
+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.
+
+------------------------------------------------------------------------------
+ALTERNATIVE B - The Unlicense
+
+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, 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 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 WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <https://unlicense.org>
+
+*/ \ No newline at end of file
diff --git a/thirdparty/openxr/COPYING.adoc b/thirdparty/openxr/COPYING.adoc
index 18c85fa6c7..d700a19d7b 100644
--- a/thirdparty/openxr/COPYING.adoc
+++ b/thirdparty/openxr/COPYING.adoc
@@ -6,7 +6,7 @@
This document is shared across a number of OpenXR GitHub projects, as the
set of files in those projects is partially overlapping.
-(There is a single "source of truth" internal Khronos GitLab repo these
+(There is a single "source of truth" internal Khronos GitLab monorepo these
GitHub repositories interact with.)
== Licenses
@@ -17,7 +17,7 @@ https://reuse.software/spec/[REUSE 3.0 specification] with clear copyright
holders and license identifier listed for each file, preferably in each
file.
Where this is not possible, or e.g. when we are using files unmodified from
-other open-source projects, license data is listed:
+other open source projects, license data is listed:
* in an adjacent file of the same name, with the additional extension
"`.license`"
diff --git a/thirdparty/openxr/include/openxr/openxr.h b/thirdparty/openxr/include/openxr/openxr.h
index 9ac66d8c69..f094b0f72b 100644
--- a/thirdparty/openxr/include/openxr/openxr.h
+++ b/thirdparty/openxr/include/openxr/openxr.h
@@ -26,7 +26,7 @@ extern "C" {
((((major) & 0xffffULL) << 48) | (((minor) & 0xffffULL) << 32) | ((patch) & 0xffffffffULL))
// OpenXR current version number.
-#define XR_CURRENT_API_VERSION XR_MAKE_VERSION(1, 1, 38)
+#define XR_CURRENT_API_VERSION XR_MAKE_VERSION(1, 1, 41)
// OpenXR 1.0 version number
#define XR_API_VERSION_1_0 XR_MAKE_VERSION(1, 0, XR_VERSION_PATCH(XR_CURRENT_API_VERSION))
@@ -242,6 +242,11 @@ typedef enum XrResult {
XR_ERROR_LOCALIZATION_MAP_PERMISSION_DENIED_ML = -1000139004,
XR_ERROR_LOCALIZATION_MAP_ALREADY_EXISTS_ML = -1000139005,
XR_ERROR_LOCALIZATION_MAP_CANNOT_EXPORT_CLOUD_MAP_ML = -1000139006,
+ XR_ERROR_SPATIAL_ANCHORS_PERMISSION_DENIED_ML = -1000140000,
+ XR_ERROR_SPATIAL_ANCHORS_NOT_LOCALIZED_ML = -1000140001,
+ XR_ERROR_SPATIAL_ANCHORS_OUT_OF_MAP_BOUNDS_ML = -1000140002,
+ XR_ERROR_SPATIAL_ANCHORS_SPACE_NOT_LOCATABLE_ML = -1000140003,
+ XR_ERROR_SPATIAL_ANCHORS_ANCHOR_NOT_FOUND_ML = -1000141000,
XR_ERROR_SPATIAL_ANCHOR_NAME_NOT_FOUND_MSFT = -1000142001,
XR_ERROR_SPATIAL_ANCHOR_NAME_INVALID_MSFT = -1000142002,
XR_SCENE_MARKER_DATA_NOT_STRING_MSFT = 1000147000,
@@ -258,6 +263,10 @@ typedef enum XrResult {
XR_ERROR_PLANE_DETECTION_PERMISSION_DENIED_EXT = -1000429001,
XR_ERROR_FUTURE_PENDING_EXT = -1000469001,
XR_ERROR_FUTURE_INVALID_EXT = -1000469002,
+ XR_ERROR_SYSTEM_NOTIFICATION_PERMISSION_DENIED_ML = -1000473000,
+ XR_ERROR_SYSTEM_NOTIFICATION_INCOMPATIBLE_SKU_ML = -1000473001,
+ XR_ERROR_WORLD_MESH_DETECTOR_PERMISSION_DENIED_ML = -1000474000,
+ XR_ERROR_WORLD_MESH_DETECTOR_SPACE_NOT_LOCATABLE_ML = -1000474001,
XR_ERROR_EXTENSION_DEPENDENCY_NOT_ENABLED_KHR = XR_ERROR_EXTENSION_DEPENDENCY_NOT_ENABLED,
XR_ERROR_PERMISSION_INSUFFICIENT_KHR = XR_ERROR_PERMISSION_INSUFFICIENT,
XR_RESULT_MAX_ENUM = 0x7FFFFFFF
@@ -346,6 +355,9 @@ typedef enum XrStructureType {
XR_TYPE_GRAPHICS_BINDING_D3D12_KHR = 1000028000,
XR_TYPE_SWAPCHAIN_IMAGE_D3D12_KHR = 1000028001,
XR_TYPE_GRAPHICS_REQUIREMENTS_D3D12_KHR = 1000028002,
+ XR_TYPE_GRAPHICS_BINDING_METAL_KHR = 1000029000,
+ XR_TYPE_SWAPCHAIN_IMAGE_METAL_KHR = 1000029001,
+ XR_TYPE_GRAPHICS_REQUIREMENTS_METAL_KHR = 1000029002,
XR_TYPE_SYSTEM_EYE_GAZE_INTERACTION_PROPERTIES_EXT = 1000030000,
XR_TYPE_EYE_GAZE_SAMPLE_TIME_EXT = 1000030001,
XR_TYPE_VISIBILITY_MASK_KHR = 1000031000,
@@ -492,6 +504,22 @@ typedef enum XrStructureType {
XR_TYPE_MAP_LOCALIZATION_REQUEST_INFO_ML = 1000139002,
XR_TYPE_LOCALIZATION_MAP_IMPORT_INFO_ML = 1000139003,
XR_TYPE_LOCALIZATION_ENABLE_EVENTS_INFO_ML = 1000139004,
+ XR_TYPE_SPATIAL_ANCHORS_CREATE_INFO_FROM_POSE_ML = 1000140000,
+ XR_TYPE_CREATE_SPATIAL_ANCHORS_COMPLETION_ML = 1000140001,
+ XR_TYPE_SPATIAL_ANCHOR_STATE_ML = 1000140002,
+ XR_TYPE_SPATIAL_ANCHORS_CREATE_STORAGE_INFO_ML = 1000141000,
+ XR_TYPE_SPATIAL_ANCHORS_QUERY_INFO_RADIUS_ML = 1000141001,
+ XR_TYPE_SPATIAL_ANCHORS_QUERY_COMPLETION_ML = 1000141002,
+ XR_TYPE_SPATIAL_ANCHORS_CREATE_INFO_FROM_UUIDS_ML = 1000141003,
+ XR_TYPE_SPATIAL_ANCHORS_PUBLISH_INFO_ML = 1000141004,
+ XR_TYPE_SPATIAL_ANCHORS_PUBLISH_COMPLETION_ML = 1000141005,
+ XR_TYPE_SPATIAL_ANCHORS_DELETE_INFO_ML = 1000141006,
+ XR_TYPE_SPATIAL_ANCHORS_DELETE_COMPLETION_ML = 1000141007,
+ XR_TYPE_SPATIAL_ANCHORS_UPDATE_EXPIRATION_INFO_ML = 1000141008,
+ XR_TYPE_SPATIAL_ANCHORS_UPDATE_EXPIRATION_COMPLETION_ML = 1000141009,
+ XR_TYPE_SPATIAL_ANCHORS_PUBLISH_COMPLETION_DETAILS_ML = 1000141010,
+ XR_TYPE_SPATIAL_ANCHORS_DELETE_COMPLETION_DETAILS_ML = 1000141011,
+ XR_TYPE_SPATIAL_ANCHORS_UPDATE_EXPIRATION_COMPLETION_DETAILS_ML = 1000141012,
XR_TYPE_EVENT_DATA_HEADSET_FIT_CHANGED_ML = 1000472000,
XR_TYPE_EVENT_DATA_EYE_CALIBRATION_CHANGED_ML = 1000472001,
XR_TYPE_USER_CALIBRATION_ENABLE_EVENTS_INFO_ML = 1000472002,
@@ -599,6 +627,11 @@ typedef enum XrStructureType {
XR_TYPE_FOVEATION_CUSTOM_MODE_INFO_HTC = 1000318002,
XR_TYPE_SYSTEM_ANCHOR_PROPERTIES_HTC = 1000319000,
XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_HTC = 1000319001,
+ XR_TYPE_SYSTEM_BODY_TRACKING_PROPERTIES_HTC = 1000320000,
+ XR_TYPE_BODY_TRACKER_CREATE_INFO_HTC = 1000320001,
+ XR_TYPE_BODY_JOINTS_LOCATE_INFO_HTC = 1000320002,
+ XR_TYPE_BODY_JOINT_LOCATIONS_HTC = 1000320003,
+ XR_TYPE_BODY_SKELETON_HTC = 1000320004,
XR_TYPE_ACTIVE_ACTION_SET_PRIORITIES_EXT = 1000373000,
XR_TYPE_SYSTEM_FORCE_FEEDBACK_CURL_PROPERTIES_MNDX = 1000375000,
XR_TYPE_FORCE_FEEDBACK_CURL_APPLY_LOCATIONS_MNDX = 1000375001,
@@ -617,6 +650,19 @@ typedef enum XrStructureType {
XR_TYPE_FUTURE_POLL_RESULT_EXT = 1000469003,
XR_TYPE_EVENT_DATA_USER_PRESENCE_CHANGED_EXT = 1000470000,
XR_TYPE_SYSTEM_USER_PRESENCE_PROPERTIES_EXT = 1000470001,
+ XR_TYPE_SYSTEM_NOTIFICATIONS_SET_INFO_ML = 1000473000,
+ XR_TYPE_WORLD_MESH_DETECTOR_CREATE_INFO_ML = 1000474001,
+ XR_TYPE_WORLD_MESH_STATE_REQUEST_INFO_ML = 1000474002,
+ XR_TYPE_WORLD_MESH_BLOCK_STATE_ML = 1000474003,
+ XR_TYPE_WORLD_MESH_STATE_REQUEST_COMPLETION_ML = 1000474004,
+ XR_TYPE_WORLD_MESH_BUFFER_RECOMMENDED_SIZE_INFO_ML = 1000474005,
+ XR_TYPE_WORLD_MESH_BUFFER_SIZE_ML = 1000474006,
+ XR_TYPE_WORLD_MESH_BUFFER_ML = 1000474007,
+ XR_TYPE_WORLD_MESH_BLOCK_REQUEST_ML = 1000474008,
+ XR_TYPE_WORLD_MESH_GET_INFO_ML = 1000474009,
+ XR_TYPE_WORLD_MESH_BLOCK_ML = 1000474010,
+ XR_TYPE_WORLD_MESH_REQUEST_COMPLETION_ML = 1000474011,
+ XR_TYPE_WORLD_MESH_REQUEST_COMPLETION_INFO_ML = 1000474012,
XR_TYPE_GRAPHICS_BINDING_VULKAN2_KHR = XR_TYPE_GRAPHICS_BINDING_VULKAN_KHR,
XR_TYPE_SWAPCHAIN_IMAGE_VULKAN2_KHR = XR_TYPE_SWAPCHAIN_IMAGE_VULKAN_KHR,
XR_TYPE_GRAPHICS_REQUIREMENTS_VULKAN2_KHR = XR_TYPE_GRAPHICS_REQUIREMENTS_VULKAN_KHR,
@@ -713,6 +759,7 @@ typedef enum XrObjectType {
XR_OBJECT_TYPE_GEOMETRY_INSTANCE_FB = 1000118004,
XR_OBJECT_TYPE_MARKER_DETECTOR_ML = 1000138000,
XR_OBJECT_TYPE_EXPORTED_LOCALIZATION_MAP_ML = 1000139000,
+ XR_OBJECT_TYPE_SPATIAL_ANCHORS_STORAGE_ML = 1000141000,
XR_OBJECT_TYPE_SPATIAL_ANCHOR_STORE_CONNECTION_MSFT = 1000142000,
XR_OBJECT_TYPE_FACE_TRACKER_FB = 1000201000,
XR_OBJECT_TYPE_EYE_TRACKER_FB = 1000202000,
@@ -723,7 +770,9 @@ typedef enum XrObjectType {
XR_OBJECT_TYPE_ENVIRONMENT_DEPTH_PROVIDER_META = 1000291000,
XR_OBJECT_TYPE_ENVIRONMENT_DEPTH_SWAPCHAIN_META = 1000291001,
XR_OBJECT_TYPE_PASSTHROUGH_HTC = 1000317000,
+ XR_OBJECT_TYPE_BODY_TRACKER_HTC = 1000320000,
XR_OBJECT_TYPE_PLANE_DETECTOR_EXT = 1000429000,
+ XR_OBJECT_TYPE_WORLD_MESH_DETECTOR_ML = 1000474000,
XR_OBJECT_TYPE_MAX_ENUM = 0x7FFFFFFF
} XrObjectType;
typedef XrFlags64 XrInstanceCreateFlags;
@@ -2184,7 +2233,7 @@ typedef struct XrEventDataMainSessionVisibilityChangedEXTX {
// XR_VARJO_quad_views is a preprocessor guard. Do not pass it to API calls.
#define XR_VARJO_quad_views 1
-#define XR_VARJO_quad_views_SPEC_VERSION 1
+#define XR_VARJO_quad_views_SPEC_VERSION 2
#define XR_VARJO_QUAD_VIEWS_EXTENSION_NAME "XR_VARJO_quad_views"
@@ -4982,6 +5031,245 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetExportedLocalizationMapDataML(
#endif /* !XR_NO_PROTOTYPES */
+// XR_ML_spatial_anchors is a preprocessor guard. Do not pass it to API calls.
+#define XR_ML_spatial_anchors 1
+XR_DEFINE_OPAQUE_64(XrFutureEXT)
+#define XR_ML_spatial_anchors_SPEC_VERSION 1
+#define XR_ML_SPATIAL_ANCHORS_EXTENSION_NAME "XR_ML_spatial_anchors"
+
+typedef enum XrSpatialAnchorConfidenceML {
+ XR_SPATIAL_ANCHOR_CONFIDENCE_LOW_ML = 0,
+ XR_SPATIAL_ANCHOR_CONFIDENCE_MEDIUM_ML = 1,
+ XR_SPATIAL_ANCHOR_CONFIDENCE_HIGH_ML = 2,
+ XR_SPATIAL_ANCHOR_CONFIDENCE_MAX_ENUM_ML = 0x7FFFFFFF
+} XrSpatialAnchorConfidenceML;
+typedef struct XR_MAY_ALIAS XrSpatialAnchorsCreateInfoBaseHeaderML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrSpatialAnchorsCreateInfoBaseHeaderML;
+
+typedef struct XrSpatialAnchorsCreateInfoFromPoseML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpace baseSpace;
+ XrPosef poseInBaseSpace;
+ XrTime time;
+} XrSpatialAnchorsCreateInfoFromPoseML;
+
+typedef struct XrCreateSpatialAnchorsCompletionML {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrResult futureResult;
+ uint32_t spaceCount;
+ XrSpace* spaces;
+} XrCreateSpatialAnchorsCompletionML;
+
+typedef struct XrSpatialAnchorStateML {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrSpatialAnchorConfidenceML confidence;
+} XrSpatialAnchorStateML;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialAnchorsAsyncML)(XrSession session, const XrSpatialAnchorsCreateInfoBaseHeaderML* createInfo, XrFutureEXT* future);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialAnchorsCompleteML)(XrSession session, XrFutureEXT future, XrCreateSpatialAnchorsCompletionML* completion);
+typedef XrResult (XRAPI_PTR *PFN_xrGetSpatialAnchorStateML)(XrSpace anchor, XrSpatialAnchorStateML* state);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialAnchorsAsyncML(
+ XrSession session,
+ const XrSpatialAnchorsCreateInfoBaseHeaderML* createInfo,
+ XrFutureEXT* future);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialAnchorsCompleteML(
+ XrSession session,
+ XrFutureEXT future,
+ XrCreateSpatialAnchorsCompletionML* completion);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetSpatialAnchorStateML(
+ XrSpace anchor,
+ XrSpatialAnchorStateML* state);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_ML_spatial_anchors_storage is a preprocessor guard. Do not pass it to API calls.
+#define XR_ML_spatial_anchors_storage 1
+XR_DEFINE_HANDLE(XrSpatialAnchorsStorageML)
+#define XR_ML_spatial_anchors_storage_SPEC_VERSION 1
+#define XR_ML_SPATIAL_ANCHORS_STORAGE_EXTENSION_NAME "XR_ML_spatial_anchors_storage"
+typedef struct XrSpatialAnchorsCreateStorageInfoML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrSpatialAnchorsCreateStorageInfoML;
+
+typedef struct XR_MAY_ALIAS XrSpatialAnchorsQueryInfoBaseHeaderML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrSpatialAnchorsQueryInfoBaseHeaderML;
+
+typedef struct XrSpatialAnchorsQueryInfoRadiusML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpace baseSpace;
+ XrVector3f center;
+ XrTime time;
+ float radius;
+} XrSpatialAnchorsQueryInfoRadiusML;
+
+typedef struct XrSpatialAnchorsQueryCompletionML {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrResult futureResult;
+ uint32_t uuidCapacityInput;
+ uint32_t uuidCountOutput;
+ XrUuidEXT* uuids;
+} XrSpatialAnchorsQueryCompletionML;
+
+typedef struct XrSpatialAnchorsCreateInfoFromUuidsML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpatialAnchorsStorageML storage;
+ uint32_t uuidCount;
+ const XrUuidEXT* uuids;
+} XrSpatialAnchorsCreateInfoFromUuidsML;
+
+typedef struct XrSpatialAnchorsPublishInfoML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t anchorCount;
+ const XrSpace* anchors;
+ uint64_t expiration;
+} XrSpatialAnchorsPublishInfoML;
+
+typedef struct XrSpatialAnchorsPublishCompletionML {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrResult futureResult;
+ uint32_t uuidCount;
+ XrUuidEXT* uuids;
+} XrSpatialAnchorsPublishCompletionML;
+
+typedef struct XrSpatialAnchorsDeleteInfoML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t uuidCount;
+ const XrUuidEXT* uuids;
+} XrSpatialAnchorsDeleteInfoML;
+
+typedef struct XrSpatialAnchorsDeleteCompletionML {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrResult futureResult;
+} XrSpatialAnchorsDeleteCompletionML;
+
+typedef struct XrSpatialAnchorsUpdateExpirationInfoML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t uuidCount;
+ const XrUuidEXT* uuids;
+ uint64_t expiration;
+} XrSpatialAnchorsUpdateExpirationInfoML;
+
+typedef struct XrSpatialAnchorsUpdateExpirationCompletionML {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrResult futureResult;
+} XrSpatialAnchorsUpdateExpirationCompletionML;
+
+typedef struct XrSpatialAnchorCompletionResultML {
+ XrUuidEXT uuid;
+ XrResult result;
+} XrSpatialAnchorCompletionResultML;
+
+// XrSpatialAnchorsPublishCompletionDetailsML extends XrSpatialAnchorsPublishCompletionML
+typedef struct XrSpatialAnchorsPublishCompletionDetailsML {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t resultCount;
+ XrSpatialAnchorCompletionResultML* results;
+} XrSpatialAnchorsPublishCompletionDetailsML;
+
+// XrSpatialAnchorsDeleteCompletionDetailsML extends XrSpatialAnchorsDeleteCompletionML
+typedef struct XrSpatialAnchorsDeleteCompletionDetailsML {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t resultCount;
+ XrSpatialAnchorCompletionResultML* results;
+} XrSpatialAnchorsDeleteCompletionDetailsML;
+
+// XrSpatialAnchorsUpdateExpirationCompletionDetailsML extends XrSpatialAnchorsUpdateExpirationCompletionML
+typedef struct XrSpatialAnchorsUpdateExpirationCompletionDetailsML {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t resultCount;
+ XrSpatialAnchorCompletionResultML* results;
+} XrSpatialAnchorsUpdateExpirationCompletionDetailsML;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialAnchorsStorageML)(XrSession session, const XrSpatialAnchorsCreateStorageInfoML* createInfo, XrSpatialAnchorsStorageML* storage);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroySpatialAnchorsStorageML)(XrSpatialAnchorsStorageML storage);
+typedef XrResult (XRAPI_PTR *PFN_xrQuerySpatialAnchorsAsyncML)(XrSpatialAnchorsStorageML storage, const XrSpatialAnchorsQueryInfoBaseHeaderML* queryInfo, XrFutureEXT* future);
+typedef XrResult (XRAPI_PTR *PFN_xrQuerySpatialAnchorsCompleteML)(XrSpatialAnchorsStorageML storage, XrFutureEXT future, XrSpatialAnchorsQueryCompletionML* completion);
+typedef XrResult (XRAPI_PTR *PFN_xrPublishSpatialAnchorsAsyncML)(XrSpatialAnchorsStorageML storage, const XrSpatialAnchorsPublishInfoML* publishInfo, XrFutureEXT* future);
+typedef XrResult (XRAPI_PTR *PFN_xrPublishSpatialAnchorsCompleteML)(XrSpatialAnchorsStorageML storage, XrFutureEXT future, XrSpatialAnchorsPublishCompletionML* completion);
+typedef XrResult (XRAPI_PTR *PFN_xrDeleteSpatialAnchorsAsyncML)(XrSpatialAnchorsStorageML storage, const XrSpatialAnchorsDeleteInfoML* deleteInfo, XrFutureEXT* future);
+typedef XrResult (XRAPI_PTR *PFN_xrDeleteSpatialAnchorsCompleteML)(XrSpatialAnchorsStorageML storage, XrFutureEXT future, XrSpatialAnchorsDeleteCompletionML* completion);
+typedef XrResult (XRAPI_PTR *PFN_xrUpdateSpatialAnchorsExpirationAsyncML)(XrSpatialAnchorsStorageML storage, const XrSpatialAnchorsUpdateExpirationInfoML* updateInfo, XrFutureEXT* future);
+typedef XrResult (XRAPI_PTR *PFN_xrUpdateSpatialAnchorsExpirationCompleteML)(XrSpatialAnchorsStorageML storage, XrFutureEXT future, XrSpatialAnchorsUpdateExpirationCompletionML* completion);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialAnchorsStorageML(
+ XrSession session,
+ const XrSpatialAnchorsCreateStorageInfoML* createInfo,
+ XrSpatialAnchorsStorageML* storage);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroySpatialAnchorsStorageML(
+ XrSpatialAnchorsStorageML storage);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrQuerySpatialAnchorsAsyncML(
+ XrSpatialAnchorsStorageML storage,
+ const XrSpatialAnchorsQueryInfoBaseHeaderML* queryInfo,
+ XrFutureEXT* future);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrQuerySpatialAnchorsCompleteML(
+ XrSpatialAnchorsStorageML storage,
+ XrFutureEXT future,
+ XrSpatialAnchorsQueryCompletionML* completion);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrPublishSpatialAnchorsAsyncML(
+ XrSpatialAnchorsStorageML storage,
+ const XrSpatialAnchorsPublishInfoML* publishInfo,
+ XrFutureEXT* future);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrPublishSpatialAnchorsCompleteML(
+ XrSpatialAnchorsStorageML storage,
+ XrFutureEXT future,
+ XrSpatialAnchorsPublishCompletionML* completion);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDeleteSpatialAnchorsAsyncML(
+ XrSpatialAnchorsStorageML storage,
+ const XrSpatialAnchorsDeleteInfoML* deleteInfo,
+ XrFutureEXT* future);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDeleteSpatialAnchorsCompleteML(
+ XrSpatialAnchorsStorageML storage,
+ XrFutureEXT future,
+ XrSpatialAnchorsDeleteCompletionML* completion);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrUpdateSpatialAnchorsExpirationAsyncML(
+ XrSpatialAnchorsStorageML storage,
+ const XrSpatialAnchorsUpdateExpirationInfoML* updateInfo,
+ XrFutureEXT* future);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrUpdateSpatialAnchorsExpirationCompleteML(
+ XrSpatialAnchorsStorageML storage,
+ XrFutureEXT future,
+ XrSpatialAnchorsUpdateExpirationCompletionML* completion);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
// XR_MSFT_spatial_anchor_persistence is a preprocessor guard. Do not pass it to API calls.
#define XR_MSFT_spatial_anchor_persistence 1
XR_DEFINE_HANDLE(XrSpatialAnchorStoreConnectionMSFT)
@@ -7052,6 +7340,131 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetSpatialAnchorNameHTC(
#endif /* !XR_NO_PROTOTYPES */
+// XR_HTC_body_tracking is a preprocessor guard. Do not pass it to API calls.
+#define XR_HTC_body_tracking 1
+
+#define XR_BODY_JOINT_COUNT_HTC 26
+
+XR_DEFINE_HANDLE(XrBodyTrackerHTC)
+#define XR_HTC_body_tracking_SPEC_VERSION 1
+#define XR_HTC_BODY_TRACKING_EXTENSION_NAME "XR_HTC_body_tracking"
+
+typedef enum XrBodyJointHTC {
+ XR_BODY_JOINT_PELVIS_HTC = 0,
+ XR_BODY_JOINT_LEFT_HIP_HTC = 1,
+ XR_BODY_JOINT_LEFT_KNEE_HTC = 2,
+ XR_BODY_JOINT_LEFT_ANKLE_HTC = 3,
+ XR_BODY_JOINT_LEFT_FEET_HTC = 4,
+ XR_BODY_JOINT_RIGHT_HIP_HTC = 5,
+ XR_BODY_JOINT_RIGHT_KNEE_HTC = 6,
+ XR_BODY_JOINT_RIGHT_ANKLE_HTC = 7,
+ XR_BODY_JOINT_RIGHT_FEET_HTC = 8,
+ XR_BODY_JOINT_WAIST_HTC = 9,
+ XR_BODY_JOINT_SPINE_LOWER_HTC = 10,
+ XR_BODY_JOINT_SPINE_MIDDLE_HTC = 11,
+ XR_BODY_JOINT_SPINE_HIGH_HTC = 12,
+ XR_BODY_JOINT_CHEST_HTC = 13,
+ XR_BODY_JOINT_NECK_HTC = 14,
+ XR_BODY_JOINT_HEAD_HTC = 15,
+ XR_BODY_JOINT_LEFT_CLAVICLE_HTC = 16,
+ XR_BODY_JOINT_LEFT_SCAPULA_HTC = 17,
+ XR_BODY_JOINT_LEFT_ARM_HTC = 18,
+ XR_BODY_JOINT_LEFT_ELBOW_HTC = 19,
+ XR_BODY_JOINT_LEFT_WRIST_HTC = 20,
+ XR_BODY_JOINT_RIGHT_CLAVICLE_HTC = 21,
+ XR_BODY_JOINT_RIGHT_SCAPULA_HTC = 22,
+ XR_BODY_JOINT_RIGHT_ARM_HTC = 23,
+ XR_BODY_JOINT_RIGHT_ELBOW_HTC = 24,
+ XR_BODY_JOINT_RIGHT_WRIST_HTC = 25,
+ XR_BODY_JOINT_MAX_ENUM_HTC = 0x7FFFFFFF
+} XrBodyJointHTC;
+
+typedef enum XrBodyJointSetHTC {
+ XR_BODY_JOINT_SET_FULL_HTC = 0,
+ XR_BODY_JOINT_SET_MAX_ENUM_HTC = 0x7FFFFFFF
+} XrBodyJointSetHTC;
+
+typedef enum XrBodyJointConfidenceHTC {
+ XR_BODY_JOINT_CONFIDENCE_NONE_HTC = 0,
+ XR_BODY_JOINT_CONFIDENCE_LOW_HTC = 1,
+ XR_BODY_JOINT_CONFIDENCE_HIGH_HTC = 2,
+ XR_BODY_JOINT_CONFIDENCE_MAX_ENUM_HTC = 0x7FFFFFFF
+} XrBodyJointConfidenceHTC;
+// XrSystemBodyTrackingPropertiesHTC extends XrSystemProperties
+typedef struct XrSystemBodyTrackingPropertiesHTC {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsBodyTracking;
+} XrSystemBodyTrackingPropertiesHTC;
+
+typedef struct XrBodyTrackerCreateInfoHTC {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrBodyJointSetHTC bodyJointSet;
+} XrBodyTrackerCreateInfoHTC;
+
+typedef struct XrBodyJointsLocateInfoHTC {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpace baseSpace;
+ XrTime time;
+} XrBodyJointsLocateInfoHTC;
+
+typedef struct XrBodyJointLocationHTC {
+ XrSpaceLocationFlags locationFlags;
+ XrPosef pose;
+} XrBodyJointLocationHTC;
+
+typedef struct XrBodyJointLocationsHTC {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrSpaceLocationFlags combinedLocationFlags;
+ XrBodyJointConfidenceHTC confidenceLevel;
+ uint32_t jointLocationCount;
+ XrBodyJointLocationHTC* jointLocations;
+ uint32_t skeletonGenerationId;
+} XrBodyJointLocationsHTC;
+
+typedef struct XrBodySkeletonJointHTC {
+ XrPosef pose;
+} XrBodySkeletonJointHTC;
+
+typedef struct XrBodySkeletonHTC {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t jointCount;
+ XrBodySkeletonJointHTC* joints;
+} XrBodySkeletonHTC;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreateBodyTrackerHTC)(XrSession session, const XrBodyTrackerCreateInfoHTC* createInfo, XrBodyTrackerHTC* bodyTracker);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroyBodyTrackerHTC)(XrBodyTrackerHTC bodyTracker);
+typedef XrResult (XRAPI_PTR *PFN_xrLocateBodyJointsHTC)(XrBodyTrackerHTC bodyTracker, const XrBodyJointsLocateInfoHTC* locateInfo, XrBodyJointLocationsHTC* locations);
+typedef XrResult (XRAPI_PTR *PFN_xrGetBodySkeletonHTC)(XrBodyTrackerHTC bodyTracker, XrSpace baseSpace, uint32_t skeletonGenerationId, XrBodySkeletonHTC* skeleton);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateBodyTrackerHTC(
+ XrSession session,
+ const XrBodyTrackerCreateInfoHTC* createInfo,
+ XrBodyTrackerHTC* bodyTracker);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroyBodyTrackerHTC(
+ XrBodyTrackerHTC bodyTracker);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrLocateBodyJointsHTC(
+ XrBodyTrackerHTC bodyTracker,
+ const XrBodyJointsLocateInfoHTC* locateInfo,
+ XrBodyJointLocationsHTC* locations);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetBodySkeletonHTC(
+ XrBodyTrackerHTC bodyTracker,
+ XrSpace baseSpace,
+ uint32_t skeletonGenerationId,
+ XrBodySkeletonHTC* skeleton);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
// XR_EXT_active_action_set_priority is a preprocessor guard. Do not pass it to API calls.
#define XR_EXT_active_action_set_priority 1
#define XR_EXT_active_action_set_priority_SPEC_VERSION 1
@@ -7313,7 +7726,6 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetPlanePolygonBufferEXT(
// XR_EXT_future is a preprocessor guard. Do not pass it to API calls.
#define XR_EXT_future 1
-XR_DEFINE_OPAQUE_64(XrFutureEXT)
#define XR_EXT_future_SPEC_VERSION 1
#define XR_EXT_FUTURE_EXTENSION_NAME "XR_EXT_future"
#define XR_NULL_FUTURE_EXT 0
@@ -7440,6 +7852,232 @@ XRAPI_ATTR XrResult XRAPI_CALL xrEnableUserCalibrationEventsML(
#endif /* !XR_NO_PROTOTYPES */
+// XR_ML_system_notifications is a preprocessor guard. Do not pass it to API calls.
+#define XR_ML_system_notifications 1
+#define XR_ML_system_notifications_SPEC_VERSION 1
+#define XR_ML_SYSTEM_NOTIFICATIONS_EXTENSION_NAME "XR_ML_system_notifications"
+typedef struct XrSystemNotificationsSetInfoML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrBool32 suppressNotifications;
+} XrSystemNotificationsSetInfoML;
+
+typedef XrResult (XRAPI_PTR *PFN_xrSetSystemNotificationsML)(XrInstance instance, const XrSystemNotificationsSetInfoML* info);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrSetSystemNotificationsML(
+ XrInstance instance,
+ const XrSystemNotificationsSetInfoML* info);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_ML_world_mesh_detection is a preprocessor guard. Do not pass it to API calls.
+#define XR_ML_world_mesh_detection 1
+XR_DEFINE_HANDLE(XrWorldMeshDetectorML)
+#define XR_ML_world_mesh_detection_SPEC_VERSION 1
+#define XR_ML_WORLD_MESH_DETECTION_EXTENSION_NAME "XR_ML_world_mesh_detection"
+
+typedef enum XrWorldMeshDetectorLodML {
+ XR_WORLD_MESH_DETECTOR_LOD_MINIMUM_ML = 0,
+ XR_WORLD_MESH_DETECTOR_LOD_MEDIUM_ML = 1,
+ XR_WORLD_MESH_DETECTOR_LOD_MAXIMUM_ML = 2,
+ XR_WORLD_MESH_DETECTOR_LOD_MAX_ENUM_ML = 0x7FFFFFFF
+} XrWorldMeshDetectorLodML;
+
+typedef enum XrWorldMeshBlockStatusML {
+ XR_WORLD_MESH_BLOCK_STATUS_NEW_ML = 0,
+ XR_WORLD_MESH_BLOCK_STATUS_UPDATED_ML = 1,
+ XR_WORLD_MESH_BLOCK_STATUS_DELETED_ML = 2,
+ XR_WORLD_MESH_BLOCK_STATUS_UNCHANGED_ML = 3,
+ XR_WORLD_MESH_BLOCK_STATUS_MAX_ENUM_ML = 0x7FFFFFFF
+} XrWorldMeshBlockStatusML;
+
+typedef enum XrWorldMeshBlockResultML {
+ XR_WORLD_MESH_BLOCK_RESULT_SUCCESS_ML = 0,
+ XR_WORLD_MESH_BLOCK_RESULT_FAILED_ML = 1,
+ XR_WORLD_MESH_BLOCK_RESULT_PENDING_ML = 2,
+ XR_WORLD_MESH_BLOCK_RESULT_PARTIAL_UPDATE_ML = 3,
+ XR_WORLD_MESH_BLOCK_RESULT_MAX_ENUM_ML = 0x7FFFFFFF
+} XrWorldMeshBlockResultML;
+typedef XrFlags64 XrWorldMeshDetectorFlagsML;
+
+// Flag bits for XrWorldMeshDetectorFlagsML
+static const XrWorldMeshDetectorFlagsML XR_WORLD_MESH_DETECTOR_POINT_CLOUD_BIT_ML = 0x00000001;
+static const XrWorldMeshDetectorFlagsML XR_WORLD_MESH_DETECTOR_COMPUTE_NORMALS_BIT_ML = 0x00000002;
+static const XrWorldMeshDetectorFlagsML XR_WORLD_MESH_DETECTOR_COMPUTE_CONFIDENCE_BIT_ML = 0x00000004;
+static const XrWorldMeshDetectorFlagsML XR_WORLD_MESH_DETECTOR_PLANARIZE_BIT_ML = 0x00000008;
+static const XrWorldMeshDetectorFlagsML XR_WORLD_MESH_DETECTOR_REMOVE_MESH_SKIRT_BIT_ML = 0x00000010;
+static const XrWorldMeshDetectorFlagsML XR_WORLD_MESH_DETECTOR_INDEX_ORDER_CW_BIT_ML = 0x00000020;
+
+typedef struct XrWorldMeshDetectorCreateInfoML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrWorldMeshDetectorCreateInfoML;
+
+typedef struct XrWorldMeshBlockStateML {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrUuidEXT uuid;
+ XrPosef meshBoundingBoxCenter;
+ XrExtent3DfEXT meshBoundingBoxExtents;
+ XrTime lastUpdateTime;
+ XrWorldMeshBlockStatusML status;
+} XrWorldMeshBlockStateML;
+
+typedef struct XrWorldMeshStateRequestInfoML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpace baseSpace;
+ XrTime time;
+ XrPosef boundingBoxCenter;
+ XrExtent3DfEXT boundingBoxExtents;
+} XrWorldMeshStateRequestInfoML;
+
+typedef struct XrWorldMeshStateRequestCompletionML {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrResult futureResult;
+ XrTime timestamp;
+ uint32_t meshBlockStateCapacityInput;
+ uint32_t meshBlockStateCountOutput;
+ XrWorldMeshBlockStateML* meshBlockStates;
+} XrWorldMeshStateRequestCompletionML;
+
+typedef struct XrWorldMeshBufferRecommendedSizeInfoML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t maxBlockCount;
+} XrWorldMeshBufferRecommendedSizeInfoML;
+
+typedef struct XrWorldMeshBufferSizeML {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t size;
+} XrWorldMeshBufferSizeML;
+
+typedef struct XrWorldMeshBufferML {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t bufferSize;
+ void* XR_MAY_ALIAS buffer;
+} XrWorldMeshBufferML;
+
+typedef struct XrWorldMeshBlockRequestML {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrUuidEXT uuid;
+ XrWorldMeshDetectorLodML lod;
+} XrWorldMeshBlockRequestML;
+
+typedef struct XrWorldMeshGetInfoML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrWorldMeshDetectorFlagsML flags;
+ float fillHoleLength;
+ float disconnectedComponentArea;
+ uint32_t blockCount;
+ XrWorldMeshBlockRequestML* blocks;
+} XrWorldMeshGetInfoML;
+
+typedef struct XrWorldMeshBlockML {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrUuidEXT uuid;
+ XrWorldMeshBlockResultML blockResult;
+ XrWorldMeshDetectorLodML lod;
+ XrWorldMeshDetectorFlagsML flags;
+ uint32_t indexCount;
+ uint16_t* indexBuffer;
+ uint32_t vertexCount;
+ XrVector3f* vertexBuffer;
+ uint32_t normalCount;
+ XrVector3f* normalBuffer;
+ uint32_t confidenceCount;
+ float* confidenceBuffer;
+} XrWorldMeshBlockML;
+
+typedef struct XrWorldMeshRequestCompletionInfoML {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpace meshSpace;
+ XrTime meshSpaceLocateTime;
+} XrWorldMeshRequestCompletionInfoML;
+
+typedef struct XrWorldMeshRequestCompletionML {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrResult futureResult;
+ uint32_t blockCount;
+ XrWorldMeshBlockML* blocks;
+} XrWorldMeshRequestCompletionML;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreateWorldMeshDetectorML)(XrSession session, const XrWorldMeshDetectorCreateInfoML* createInfo, XrWorldMeshDetectorML* detector);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroyWorldMeshDetectorML)(XrWorldMeshDetectorML detector);
+typedef XrResult (XRAPI_PTR *PFN_xrRequestWorldMeshStateAsyncML)(XrWorldMeshDetectorML detector, const XrWorldMeshStateRequestInfoML* stateRequest, XrFutureEXT* future);
+typedef XrResult (XRAPI_PTR *PFN_xrRequestWorldMeshStateCompleteML)(XrWorldMeshDetectorML detector, XrFutureEXT future, XrWorldMeshStateRequestCompletionML* completion);
+typedef XrResult (XRAPI_PTR *PFN_xrGetWorldMeshBufferRecommendSizeML)(XrWorldMeshDetectorML detector, const XrWorldMeshBufferRecommendedSizeInfoML* sizeInfo, XrWorldMeshBufferSizeML* size);
+typedef XrResult (XRAPI_PTR *PFN_xrAllocateWorldMeshBufferML)(XrWorldMeshDetectorML detector, const XrWorldMeshBufferSizeML* size, XrWorldMeshBufferML* buffer);
+typedef XrResult (XRAPI_PTR *PFN_xrFreeWorldMeshBufferML)(XrWorldMeshDetectorML detector, const XrWorldMeshBufferML* buffer);
+typedef XrResult (XRAPI_PTR *PFN_xrRequestWorldMeshAsyncML)(XrWorldMeshDetectorML detector, const XrWorldMeshGetInfoML* getInfo, XrWorldMeshBufferML* buffer, XrFutureEXT* future);
+typedef XrResult (XRAPI_PTR *PFN_xrRequestWorldMeshCompleteML)(XrWorldMeshDetectorML detector, const XrWorldMeshRequestCompletionInfoML* completionInfo, XrFutureEXT future, XrWorldMeshRequestCompletionML* completion);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateWorldMeshDetectorML(
+ XrSession session,
+ const XrWorldMeshDetectorCreateInfoML* createInfo,
+ XrWorldMeshDetectorML* detector);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroyWorldMeshDetectorML(
+ XrWorldMeshDetectorML detector);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrRequestWorldMeshStateAsyncML(
+ XrWorldMeshDetectorML detector,
+ const XrWorldMeshStateRequestInfoML* stateRequest,
+ XrFutureEXT* future);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrRequestWorldMeshStateCompleteML(
+ XrWorldMeshDetectorML detector,
+ XrFutureEXT future,
+ XrWorldMeshStateRequestCompletionML* completion);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetWorldMeshBufferRecommendSizeML(
+ XrWorldMeshDetectorML detector,
+ const XrWorldMeshBufferRecommendedSizeInfoML* sizeInfo,
+ XrWorldMeshBufferSizeML* size);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrAllocateWorldMeshBufferML(
+ XrWorldMeshDetectorML detector,
+ const XrWorldMeshBufferSizeML* size,
+ XrWorldMeshBufferML* buffer);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrFreeWorldMeshBufferML(
+ XrWorldMeshDetectorML detector,
+ const XrWorldMeshBufferML* buffer);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrRequestWorldMeshAsyncML(
+ XrWorldMeshDetectorML detector,
+ const XrWorldMeshGetInfoML* getInfo,
+ XrWorldMeshBufferML* buffer,
+ XrFutureEXT* future);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrRequestWorldMeshCompleteML(
+ XrWorldMeshDetectorML detector,
+ const XrWorldMeshRequestCompletionInfoML* completionInfo,
+ XrFutureEXT future,
+ XrWorldMeshRequestCompletionML* completion);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_ML_view_configuration_depth_range_change is a preprocessor guard. Do not pass it to API calls.
+#define XR_ML_view_configuration_depth_range_change 1
+#define XR_ML_view_configuration_depth_range_change_SPEC_VERSION 1
+#define XR_ML_VIEW_CONFIGURATION_DEPTH_RANGE_CHANGE_EXTENSION_NAME "XR_ML_view_configuration_depth_range_change"
+
+
// XR_YVR_controller_interaction is a preprocessor guard. Do not pass it to API calls.
#define XR_YVR_controller_interaction 1
#define XR_YVR_controller_interaction_SPEC_VERSION 1
diff --git a/thirdparty/openxr/include/openxr/openxr_platform.h b/thirdparty/openxr/include/openxr/openxr_platform.h
index cbe1008906..dfd74aa5d2 100644
--- a/thirdparty/openxr/include/openxr/openxr_platform.h
+++ b/thirdparty/openxr/include/openxr/openxr_platform.h
@@ -356,6 +356,43 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetD3D12GraphicsRequirementsKHR(
#endif /* !XR_NO_PROTOTYPES */
#endif /* XR_USE_GRAPHICS_API_D3D12 */
+#ifdef XR_USE_GRAPHICS_API_METAL
+
+// XR_KHR_metal_enable is a preprocessor guard. Do not pass it to API calls.
+#define XR_KHR_metal_enable 1
+#define XR_KHR_metal_enable_SPEC_VERSION 1
+#define XR_KHR_METAL_ENABLE_EXTENSION_NAME "XR_KHR_metal_enable"
+// XrGraphicsBindingMetalKHR extends XrSessionCreateInfo
+typedef struct XrGraphicsBindingMetalKHR {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ void* XR_MAY_ALIAS commandQueue;
+} XrGraphicsBindingMetalKHR;
+
+typedef struct XrSwapchainImageMetalKHR {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ void* XR_MAY_ALIAS texture;
+} XrSwapchainImageMetalKHR;
+
+typedef struct XrGraphicsRequirementsMetalKHR {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ void* XR_MAY_ALIAS metalDevice;
+} XrGraphicsRequirementsMetalKHR;
+
+typedef XrResult (XRAPI_PTR *PFN_xrGetMetalGraphicsRequirementsKHR)(XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsMetalKHR* graphicsRequirements);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrGetMetalGraphicsRequirementsKHR(
+ XrInstance instance,
+ XrSystemId systemId,
+ XrGraphicsRequirementsMetalKHR* graphicsRequirements);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+#endif /* XR_USE_GRAPHICS_API_METAL */
+
#ifdef XR_USE_PLATFORM_WIN32
// XR_KHR_win32_convert_performance_counter_time is a preprocessor guard. Do not pass it to API calls.
diff --git a/thirdparty/openxr/include/openxr/openxr_reflection.h b/thirdparty/openxr/include/openxr/openxr_reflection.h
index b9d45689a7..4bea81c236 100644
--- a/thirdparty/openxr/include/openxr/openxr_reflection.h
+++ b/thirdparty/openxr/include/openxr/openxr_reflection.h
@@ -128,6 +128,11 @@ XR_ENUM_STR(XrResult);
_(XR_ERROR_LOCALIZATION_MAP_PERMISSION_DENIED_ML, -1000139004) \
_(XR_ERROR_LOCALIZATION_MAP_ALREADY_EXISTS_ML, -1000139005) \
_(XR_ERROR_LOCALIZATION_MAP_CANNOT_EXPORT_CLOUD_MAP_ML, -1000139006) \
+ _(XR_ERROR_SPATIAL_ANCHORS_PERMISSION_DENIED_ML, -1000140000) \
+ _(XR_ERROR_SPATIAL_ANCHORS_NOT_LOCALIZED_ML, -1000140001) \
+ _(XR_ERROR_SPATIAL_ANCHORS_OUT_OF_MAP_BOUNDS_ML, -1000140002) \
+ _(XR_ERROR_SPATIAL_ANCHORS_SPACE_NOT_LOCATABLE_ML, -1000140003) \
+ _(XR_ERROR_SPATIAL_ANCHORS_ANCHOR_NOT_FOUND_ML, -1000141000) \
_(XR_ERROR_SPATIAL_ANCHOR_NAME_NOT_FOUND_MSFT, -1000142001) \
_(XR_ERROR_SPATIAL_ANCHOR_NAME_INVALID_MSFT, -1000142002) \
_(XR_SCENE_MARKER_DATA_NOT_STRING_MSFT, 1000147000) \
@@ -144,6 +149,10 @@ XR_ENUM_STR(XrResult);
_(XR_ERROR_PLANE_DETECTION_PERMISSION_DENIED_EXT, -1000429001) \
_(XR_ERROR_FUTURE_PENDING_EXT, -1000469001) \
_(XR_ERROR_FUTURE_INVALID_EXT, -1000469002) \
+ _(XR_ERROR_SYSTEM_NOTIFICATION_PERMISSION_DENIED_ML, -1000473000) \
+ _(XR_ERROR_SYSTEM_NOTIFICATION_INCOMPATIBLE_SKU_ML, -1000473001) \
+ _(XR_ERROR_WORLD_MESH_DETECTOR_PERMISSION_DENIED_ML, -1000474000) \
+ _(XR_ERROR_WORLD_MESH_DETECTOR_SPACE_NOT_LOCATABLE_ML, -1000474001) \
_(XR_RESULT_MAX_ENUM, 0x7FFFFFFF)
#define XR_LIST_ENUM_XrStructureType(_) \
@@ -229,6 +238,9 @@ XR_ENUM_STR(XrResult);
_(XR_TYPE_GRAPHICS_BINDING_D3D12_KHR, 1000028000) \
_(XR_TYPE_SWAPCHAIN_IMAGE_D3D12_KHR, 1000028001) \
_(XR_TYPE_GRAPHICS_REQUIREMENTS_D3D12_KHR, 1000028002) \
+ _(XR_TYPE_GRAPHICS_BINDING_METAL_KHR, 1000029000) \
+ _(XR_TYPE_SWAPCHAIN_IMAGE_METAL_KHR, 1000029001) \
+ _(XR_TYPE_GRAPHICS_REQUIREMENTS_METAL_KHR, 1000029002) \
_(XR_TYPE_SYSTEM_EYE_GAZE_INTERACTION_PROPERTIES_EXT, 1000030000) \
_(XR_TYPE_EYE_GAZE_SAMPLE_TIME_EXT, 1000030001) \
_(XR_TYPE_VISIBILITY_MASK_KHR, 1000031000) \
@@ -375,6 +387,22 @@ XR_ENUM_STR(XrResult);
_(XR_TYPE_MAP_LOCALIZATION_REQUEST_INFO_ML, 1000139002) \
_(XR_TYPE_LOCALIZATION_MAP_IMPORT_INFO_ML, 1000139003) \
_(XR_TYPE_LOCALIZATION_ENABLE_EVENTS_INFO_ML, 1000139004) \
+ _(XR_TYPE_SPATIAL_ANCHORS_CREATE_INFO_FROM_POSE_ML, 1000140000) \
+ _(XR_TYPE_CREATE_SPATIAL_ANCHORS_COMPLETION_ML, 1000140001) \
+ _(XR_TYPE_SPATIAL_ANCHOR_STATE_ML, 1000140002) \
+ _(XR_TYPE_SPATIAL_ANCHORS_CREATE_STORAGE_INFO_ML, 1000141000) \
+ _(XR_TYPE_SPATIAL_ANCHORS_QUERY_INFO_RADIUS_ML, 1000141001) \
+ _(XR_TYPE_SPATIAL_ANCHORS_QUERY_COMPLETION_ML, 1000141002) \
+ _(XR_TYPE_SPATIAL_ANCHORS_CREATE_INFO_FROM_UUIDS_ML, 1000141003) \
+ _(XR_TYPE_SPATIAL_ANCHORS_PUBLISH_INFO_ML, 1000141004) \
+ _(XR_TYPE_SPATIAL_ANCHORS_PUBLISH_COMPLETION_ML, 1000141005) \
+ _(XR_TYPE_SPATIAL_ANCHORS_DELETE_INFO_ML, 1000141006) \
+ _(XR_TYPE_SPATIAL_ANCHORS_DELETE_COMPLETION_ML, 1000141007) \
+ _(XR_TYPE_SPATIAL_ANCHORS_UPDATE_EXPIRATION_INFO_ML, 1000141008) \
+ _(XR_TYPE_SPATIAL_ANCHORS_UPDATE_EXPIRATION_COMPLETION_ML, 1000141009) \
+ _(XR_TYPE_SPATIAL_ANCHORS_PUBLISH_COMPLETION_DETAILS_ML, 1000141010) \
+ _(XR_TYPE_SPATIAL_ANCHORS_DELETE_COMPLETION_DETAILS_ML, 1000141011) \
+ _(XR_TYPE_SPATIAL_ANCHORS_UPDATE_EXPIRATION_COMPLETION_DETAILS_ML, 1000141012) \
_(XR_TYPE_EVENT_DATA_HEADSET_FIT_CHANGED_ML, 1000472000) \
_(XR_TYPE_EVENT_DATA_EYE_CALIBRATION_CHANGED_ML, 1000472001) \
_(XR_TYPE_USER_CALIBRATION_ENABLE_EVENTS_INFO_ML, 1000472002) \
@@ -482,6 +510,11 @@ XR_ENUM_STR(XrResult);
_(XR_TYPE_FOVEATION_CUSTOM_MODE_INFO_HTC, 1000318002) \
_(XR_TYPE_SYSTEM_ANCHOR_PROPERTIES_HTC, 1000319000) \
_(XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_HTC, 1000319001) \
+ _(XR_TYPE_SYSTEM_BODY_TRACKING_PROPERTIES_HTC, 1000320000) \
+ _(XR_TYPE_BODY_TRACKER_CREATE_INFO_HTC, 1000320001) \
+ _(XR_TYPE_BODY_JOINTS_LOCATE_INFO_HTC, 1000320002) \
+ _(XR_TYPE_BODY_JOINT_LOCATIONS_HTC, 1000320003) \
+ _(XR_TYPE_BODY_SKELETON_HTC, 1000320004) \
_(XR_TYPE_ACTIVE_ACTION_SET_PRIORITIES_EXT, 1000373000) \
_(XR_TYPE_SYSTEM_FORCE_FEEDBACK_CURL_PROPERTIES_MNDX, 1000375000) \
_(XR_TYPE_FORCE_FEEDBACK_CURL_APPLY_LOCATIONS_MNDX, 1000375001) \
@@ -500,6 +533,19 @@ XR_ENUM_STR(XrResult);
_(XR_TYPE_FUTURE_POLL_RESULT_EXT, 1000469003) \
_(XR_TYPE_EVENT_DATA_USER_PRESENCE_CHANGED_EXT, 1000470000) \
_(XR_TYPE_SYSTEM_USER_PRESENCE_PROPERTIES_EXT, 1000470001) \
+ _(XR_TYPE_SYSTEM_NOTIFICATIONS_SET_INFO_ML, 1000473000) \
+ _(XR_TYPE_WORLD_MESH_DETECTOR_CREATE_INFO_ML, 1000474001) \
+ _(XR_TYPE_WORLD_MESH_STATE_REQUEST_INFO_ML, 1000474002) \
+ _(XR_TYPE_WORLD_MESH_BLOCK_STATE_ML, 1000474003) \
+ _(XR_TYPE_WORLD_MESH_STATE_REQUEST_COMPLETION_ML, 1000474004) \
+ _(XR_TYPE_WORLD_MESH_BUFFER_RECOMMENDED_SIZE_INFO_ML, 1000474005) \
+ _(XR_TYPE_WORLD_MESH_BUFFER_SIZE_ML, 1000474006) \
+ _(XR_TYPE_WORLD_MESH_BUFFER_ML, 1000474007) \
+ _(XR_TYPE_WORLD_MESH_BLOCK_REQUEST_ML, 1000474008) \
+ _(XR_TYPE_WORLD_MESH_GET_INFO_ML, 1000474009) \
+ _(XR_TYPE_WORLD_MESH_BLOCK_ML, 1000474010) \
+ _(XR_TYPE_WORLD_MESH_REQUEST_COMPLETION_ML, 1000474011) \
+ _(XR_TYPE_WORLD_MESH_REQUEST_COMPLETION_INFO_ML, 1000474012) \
_(XR_STRUCTURE_TYPE_MAX_ENUM, 0x7FFFFFFF)
#define XR_LIST_ENUM_XrFormFactor(_) \
@@ -579,6 +625,7 @@ XR_ENUM_STR(XrResult);
_(XR_OBJECT_TYPE_GEOMETRY_INSTANCE_FB, 1000118004) \
_(XR_OBJECT_TYPE_MARKER_DETECTOR_ML, 1000138000) \
_(XR_OBJECT_TYPE_EXPORTED_LOCALIZATION_MAP_ML, 1000139000) \
+ _(XR_OBJECT_TYPE_SPATIAL_ANCHORS_STORAGE_ML, 1000141000) \
_(XR_OBJECT_TYPE_SPATIAL_ANCHOR_STORE_CONNECTION_MSFT, 1000142000) \
_(XR_OBJECT_TYPE_FACE_TRACKER_FB, 1000201000) \
_(XR_OBJECT_TYPE_EYE_TRACKER_FB, 1000202000) \
@@ -589,7 +636,9 @@ XR_ENUM_STR(XrResult);
_(XR_OBJECT_TYPE_ENVIRONMENT_DEPTH_PROVIDER_META, 1000291000) \
_(XR_OBJECT_TYPE_ENVIRONMENT_DEPTH_SWAPCHAIN_META, 1000291001) \
_(XR_OBJECT_TYPE_PASSTHROUGH_HTC, 1000317000) \
+ _(XR_OBJECT_TYPE_BODY_TRACKER_HTC, 1000320000) \
_(XR_OBJECT_TYPE_PLANE_DETECTOR_EXT, 1000429000) \
+ _(XR_OBJECT_TYPE_WORLD_MESH_DETECTOR_ML, 1000474000) \
_(XR_OBJECT_TYPE_MAX_ENUM, 0x7FFFFFFF)
#define XR_LIST_ENUM_XrLoaderInterfaceStructs(_) \
@@ -1053,6 +1102,12 @@ XR_ENUM_STR(XrResult);
_(XR_LOCALIZATION_MAP_CONFIDENCE_EXCELLENT_ML, 3) \
_(XR_LOCALIZATION_MAP_CONFIDENCE_MAX_ENUM_ML, 0x7FFFFFFF)
+#define XR_LIST_ENUM_XrSpatialAnchorConfidenceML(_) \
+ _(XR_SPATIAL_ANCHOR_CONFIDENCE_LOW_ML, 0) \
+ _(XR_SPATIAL_ANCHOR_CONFIDENCE_MEDIUM_ML, 1) \
+ _(XR_SPATIAL_ANCHOR_CONFIDENCE_HIGH_ML, 2) \
+ _(XR_SPATIAL_ANCHOR_CONFIDENCE_MAX_ENUM_ML, 0x7FFFFFFF)
+
#define XR_LIST_ENUM_XrSceneMarkerTypeMSFT(_) \
_(XR_SCENE_MARKER_TYPE_QR_CODE_MSFT, 1) \
_(XR_SCENE_MARKER_TYPE_MAX_ENUM_MSFT, 0x7FFFFFFF)
@@ -1363,6 +1418,45 @@ XR_ENUM_STR(XrResult);
_(XR_FOVEATION_LEVEL_HIGH_HTC, 3) \
_(XR_FOVEATION_LEVEL_MAX_ENUM_HTC, 0x7FFFFFFF)
+#define XR_LIST_ENUM_XrBodyJointHTC(_) \
+ _(XR_BODY_JOINT_PELVIS_HTC, 0) \
+ _(XR_BODY_JOINT_LEFT_HIP_HTC, 1) \
+ _(XR_BODY_JOINT_LEFT_KNEE_HTC, 2) \
+ _(XR_BODY_JOINT_LEFT_ANKLE_HTC, 3) \
+ _(XR_BODY_JOINT_LEFT_FEET_HTC, 4) \
+ _(XR_BODY_JOINT_RIGHT_HIP_HTC, 5) \
+ _(XR_BODY_JOINT_RIGHT_KNEE_HTC, 6) \
+ _(XR_BODY_JOINT_RIGHT_ANKLE_HTC, 7) \
+ _(XR_BODY_JOINT_RIGHT_FEET_HTC, 8) \
+ _(XR_BODY_JOINT_WAIST_HTC, 9) \
+ _(XR_BODY_JOINT_SPINE_LOWER_HTC, 10) \
+ _(XR_BODY_JOINT_SPINE_MIDDLE_HTC, 11) \
+ _(XR_BODY_JOINT_SPINE_HIGH_HTC, 12) \
+ _(XR_BODY_JOINT_CHEST_HTC, 13) \
+ _(XR_BODY_JOINT_NECK_HTC, 14) \
+ _(XR_BODY_JOINT_HEAD_HTC, 15) \
+ _(XR_BODY_JOINT_LEFT_CLAVICLE_HTC, 16) \
+ _(XR_BODY_JOINT_LEFT_SCAPULA_HTC, 17) \
+ _(XR_BODY_JOINT_LEFT_ARM_HTC, 18) \
+ _(XR_BODY_JOINT_LEFT_ELBOW_HTC, 19) \
+ _(XR_BODY_JOINT_LEFT_WRIST_HTC, 20) \
+ _(XR_BODY_JOINT_RIGHT_CLAVICLE_HTC, 21) \
+ _(XR_BODY_JOINT_RIGHT_SCAPULA_HTC, 22) \
+ _(XR_BODY_JOINT_RIGHT_ARM_HTC, 23) \
+ _(XR_BODY_JOINT_RIGHT_ELBOW_HTC, 24) \
+ _(XR_BODY_JOINT_RIGHT_WRIST_HTC, 25) \
+ _(XR_BODY_JOINT_MAX_ENUM_HTC, 0x7FFFFFFF)
+
+#define XR_LIST_ENUM_XrBodyJointSetHTC(_) \
+ _(XR_BODY_JOINT_SET_FULL_HTC, 0) \
+ _(XR_BODY_JOINT_SET_MAX_ENUM_HTC, 0x7FFFFFFF)
+
+#define XR_LIST_ENUM_XrBodyJointConfidenceHTC(_) \
+ _(XR_BODY_JOINT_CONFIDENCE_NONE_HTC, 0) \
+ _(XR_BODY_JOINT_CONFIDENCE_LOW_HTC, 1) \
+ _(XR_BODY_JOINT_CONFIDENCE_HIGH_HTC, 2) \
+ _(XR_BODY_JOINT_CONFIDENCE_MAX_ENUM_HTC, 0x7FFFFFFF)
+
#define XR_LIST_ENUM_XrForceFeedbackCurlLocationMNDX(_) \
_(XR_FORCE_FEEDBACK_CURL_LOCATION_THUMB_CURL_MNDX, 0) \
_(XR_FORCE_FEEDBACK_CURL_LOCATION_INDEX_CURL_MNDX, 1) \
@@ -1418,6 +1512,26 @@ XR_ENUM_STR(XrResult);
_(XR_EYE_CALIBRATION_STATUS_FINE_ML, 3) \
_(XR_EYE_CALIBRATION_STATUS_MAX_ENUM_ML, 0x7FFFFFFF)
+#define XR_LIST_ENUM_XrWorldMeshDetectorLodML(_) \
+ _(XR_WORLD_MESH_DETECTOR_LOD_MINIMUM_ML, 0) \
+ _(XR_WORLD_MESH_DETECTOR_LOD_MEDIUM_ML, 1) \
+ _(XR_WORLD_MESH_DETECTOR_LOD_MAXIMUM_ML, 2) \
+ _(XR_WORLD_MESH_DETECTOR_LOD_MAX_ENUM_ML, 0x7FFFFFFF)
+
+#define XR_LIST_ENUM_XrWorldMeshBlockStatusML(_) \
+ _(XR_WORLD_MESH_BLOCK_STATUS_NEW_ML, 0) \
+ _(XR_WORLD_MESH_BLOCK_STATUS_UPDATED_ML, 1) \
+ _(XR_WORLD_MESH_BLOCK_STATUS_DELETED_ML, 2) \
+ _(XR_WORLD_MESH_BLOCK_STATUS_UNCHANGED_ML, 3) \
+ _(XR_WORLD_MESH_BLOCK_STATUS_MAX_ENUM_ML, 0x7FFFFFFF)
+
+#define XR_LIST_ENUM_XrWorldMeshBlockResultML(_) \
+ _(XR_WORLD_MESH_BLOCK_RESULT_SUCCESS_ML, 0) \
+ _(XR_WORLD_MESH_BLOCK_RESULT_FAILED_ML, 1) \
+ _(XR_WORLD_MESH_BLOCK_RESULT_PENDING_ML, 2) \
+ _(XR_WORLD_MESH_BLOCK_RESULT_PARTIAL_UPDATE_ML, 3) \
+ _(XR_WORLD_MESH_BLOCK_RESULT_MAX_ENUM_ML, 0x7FFFFFFF)
+
#define XR_LIST_BITS_XrInstanceCreateFlags(_)
#define XR_LIST_BITS_XrSessionCreateFlags(_)
@@ -1622,6 +1736,14 @@ XR_ENUM_STR(XrResult);
#define XR_LIST_BITS_XrPlaneDetectorFlagsEXT(_) \
_(XR_PLANE_DETECTOR_ENABLE_CONTOUR_BIT_EXT, 0x00000001) \
+#define XR_LIST_BITS_XrWorldMeshDetectorFlagsML(_) \
+ _(XR_WORLD_MESH_DETECTOR_POINT_CLOUD_BIT_ML, 0x00000001) \
+ _(XR_WORLD_MESH_DETECTOR_COMPUTE_NORMALS_BIT_ML, 0x00000002) \
+ _(XR_WORLD_MESH_DETECTOR_COMPUTE_CONFIDENCE_BIT_ML, 0x00000004) \
+ _(XR_WORLD_MESH_DETECTOR_PLANARIZE_BIT_ML, 0x00000008) \
+ _(XR_WORLD_MESH_DETECTOR_REMOVE_MESH_SKIRT_BIT_ML, 0x00000010) \
+ _(XR_WORLD_MESH_DETECTOR_INDEX_ORDER_CW_BIT_ML, 0x00000020) \
+
/// Calls your macro with the name of each member of XrApiLayerProperties, in order.
#define XR_LIST_STRUCT_XrApiLayerProperties(_) \
_(type) \
@@ -2442,6 +2564,24 @@ XR_ENUM_STR(XrResult);
_(adapterLuid) \
_(minFeatureLevel) \
+/// Calls your macro with the name of each member of XrGraphicsBindingMetalKHR, in order.
+#define XR_LIST_STRUCT_XrGraphicsBindingMetalKHR(_) \
+ _(type) \
+ _(next) \
+ _(commandQueue) \
+
+/// Calls your macro with the name of each member of XrSwapchainImageMetalKHR, in order.
+#define XR_LIST_STRUCT_XrSwapchainImageMetalKHR(_) \
+ _(type) \
+ _(next) \
+ _(texture) \
+
+/// Calls your macro with the name of each member of XrGraphicsRequirementsMetalKHR, in order.
+#define XR_LIST_STRUCT_XrGraphicsRequirementsMetalKHR(_) \
+ _(type) \
+ _(next) \
+ _(metalDevice) \
+
/// Calls your macro with the name of each member of XrVisibilityMaskKHR, in order.
#define XR_LIST_STRUCT_XrVisibilityMaskKHR(_) \
_(type) \
@@ -3711,6 +3851,138 @@ XR_ENUM_STR(XrResult);
_(next) \
_(enabled) \
+/// Calls your macro with the name of each member of XrSpatialAnchorsCreateInfoBaseHeaderML, in order.
+#define XR_LIST_STRUCT_XrSpatialAnchorsCreateInfoBaseHeaderML(_) \
+ _(type) \
+ _(next) \
+
+/// Calls your macro with the name of each member of XrSpatialAnchorsCreateInfoFromPoseML, in order.
+#define XR_LIST_STRUCT_XrSpatialAnchorsCreateInfoFromPoseML(_) \
+ _(type) \
+ _(next) \
+ _(baseSpace) \
+ _(poseInBaseSpace) \
+ _(time) \
+
+/// Calls your macro with the name of each member of XrCreateSpatialAnchorsCompletionML, in order.
+#define XR_LIST_STRUCT_XrCreateSpatialAnchorsCompletionML(_) \
+ _(type) \
+ _(next) \
+ _(futureResult) \
+ _(spaceCount) \
+ _(spaces) \
+
+/// Calls your macro with the name of each member of XrSpatialAnchorStateML, in order.
+#define XR_LIST_STRUCT_XrSpatialAnchorStateML(_) \
+ _(type) \
+ _(next) \
+ _(confidence) \
+
+/// Calls your macro with the name of each member of XrSpatialAnchorsCreateStorageInfoML, in order.
+#define XR_LIST_STRUCT_XrSpatialAnchorsCreateStorageInfoML(_) \
+ _(type) \
+ _(next) \
+
+/// Calls your macro with the name of each member of XrSpatialAnchorsQueryInfoBaseHeaderML, in order.
+#define XR_LIST_STRUCT_XrSpatialAnchorsQueryInfoBaseHeaderML(_) \
+ _(type) \
+ _(next) \
+
+/// Calls your macro with the name of each member of XrSpatialAnchorsQueryInfoRadiusML, in order.
+#define XR_LIST_STRUCT_XrSpatialAnchorsQueryInfoRadiusML(_) \
+ _(type) \
+ _(next) \
+ _(baseSpace) \
+ _(center) \
+ _(time) \
+ _(radius) \
+
+/// Calls your macro with the name of each member of XrSpatialAnchorsQueryCompletionML, in order.
+#define XR_LIST_STRUCT_XrSpatialAnchorsQueryCompletionML(_) \
+ _(type) \
+ _(next) \
+ _(futureResult) \
+ _(uuidCapacityInput) \
+ _(uuidCountOutput) \
+ _(uuids) \
+
+/// Calls your macro with the name of each member of XrSpatialAnchorsCreateInfoFromUuidsML, in order.
+#define XR_LIST_STRUCT_XrSpatialAnchorsCreateInfoFromUuidsML(_) \
+ _(type) \
+ _(next) \
+ _(storage) \
+ _(uuidCount) \
+ _(uuids) \
+
+/// Calls your macro with the name of each member of XrSpatialAnchorsPublishInfoML, in order.
+#define XR_LIST_STRUCT_XrSpatialAnchorsPublishInfoML(_) \
+ _(type) \
+ _(next) \
+ _(anchorCount) \
+ _(anchors) \
+ _(expiration) \
+
+/// Calls your macro with the name of each member of XrSpatialAnchorsPublishCompletionML, in order.
+#define XR_LIST_STRUCT_XrSpatialAnchorsPublishCompletionML(_) \
+ _(type) \
+ _(next) \
+ _(futureResult) \
+ _(uuidCount) \
+ _(uuids) \
+
+/// Calls your macro with the name of each member of XrSpatialAnchorsDeleteInfoML, in order.
+#define XR_LIST_STRUCT_XrSpatialAnchorsDeleteInfoML(_) \
+ _(type) \
+ _(next) \
+ _(uuidCount) \
+ _(uuids) \
+
+/// Calls your macro with the name of each member of XrSpatialAnchorsDeleteCompletionML, in order.
+#define XR_LIST_STRUCT_XrSpatialAnchorsDeleteCompletionML(_) \
+ _(type) \
+ _(next) \
+ _(futureResult) \
+
+/// Calls your macro with the name of each member of XrSpatialAnchorsUpdateExpirationInfoML, in order.
+#define XR_LIST_STRUCT_XrSpatialAnchorsUpdateExpirationInfoML(_) \
+ _(type) \
+ _(next) \
+ _(uuidCount) \
+ _(uuids) \
+ _(expiration) \
+
+/// Calls your macro with the name of each member of XrSpatialAnchorsUpdateExpirationCompletionML, in order.
+#define XR_LIST_STRUCT_XrSpatialAnchorsUpdateExpirationCompletionML(_) \
+ _(type) \
+ _(next) \
+ _(futureResult) \
+
+/// Calls your macro with the name of each member of XrSpatialAnchorCompletionResultML, in order.
+#define XR_LIST_STRUCT_XrSpatialAnchorCompletionResultML(_) \
+ _(uuid) \
+ _(result) \
+
+/// Calls your macro with the name of each member of XrSpatialAnchorsPublishCompletionDetailsML, in order.
+#define XR_LIST_STRUCT_XrSpatialAnchorsPublishCompletionDetailsML(_) \
+ _(type) \
+ _(next) \
+ _(resultCount) \
+ _(results) \
+
+/// Calls your macro with the name of each member of XrSpatialAnchorsDeleteCompletionDetailsML, in order.
+#define XR_LIST_STRUCT_XrSpatialAnchorsDeleteCompletionDetailsML(_) \
+ _(type) \
+ _(next) \
+ _(resultCount) \
+ _(results) \
+
+/// Calls your macro with the name of each member of XrSpatialAnchorsUpdateExpirationCompletionDetailsML, in order.
+#define XR_LIST_STRUCT_XrSpatialAnchorsUpdateExpirationCompletionDetailsML(_) \
+ _(type) \
+ _(next) \
+ _(resultCount) \
+ _(results) \
+
/// Calls your macro with the name of each member of XrSpatialAnchorPersistenceNameMSFT, in order.
#define XR_LIST_STRUCT_XrSpatialAnchorPersistenceNameMSFT(_) \
_(name) \
@@ -4571,6 +4843,51 @@ XR_ENUM_STR(XrResult);
_(poseInSpace) \
_(name) \
+/// Calls your macro with the name of each member of XrSystemBodyTrackingPropertiesHTC, in order.
+#define XR_LIST_STRUCT_XrSystemBodyTrackingPropertiesHTC(_) \
+ _(type) \
+ _(next) \
+ _(supportsBodyTracking) \
+
+/// Calls your macro with the name of each member of XrBodyTrackerCreateInfoHTC, in order.
+#define XR_LIST_STRUCT_XrBodyTrackerCreateInfoHTC(_) \
+ _(type) \
+ _(next) \
+ _(bodyJointSet) \
+
+/// Calls your macro with the name of each member of XrBodyJointsLocateInfoHTC, in order.
+#define XR_LIST_STRUCT_XrBodyJointsLocateInfoHTC(_) \
+ _(type) \
+ _(next) \
+ _(baseSpace) \
+ _(time) \
+
+/// Calls your macro with the name of each member of XrBodyJointLocationHTC, in order.
+#define XR_LIST_STRUCT_XrBodyJointLocationHTC(_) \
+ _(locationFlags) \
+ _(pose) \
+
+/// Calls your macro with the name of each member of XrBodyJointLocationsHTC, in order.
+#define XR_LIST_STRUCT_XrBodyJointLocationsHTC(_) \
+ _(type) \
+ _(next) \
+ _(combinedLocationFlags) \
+ _(confidenceLevel) \
+ _(jointLocationCount) \
+ _(jointLocations) \
+ _(skeletonGenerationId) \
+
+/// Calls your macro with the name of each member of XrBodySkeletonJointHTC, in order.
+#define XR_LIST_STRUCT_XrBodySkeletonJointHTC(_) \
+ _(pose) \
+
+/// Calls your macro with the name of each member of XrBodySkeletonHTC, in order.
+#define XR_LIST_STRUCT_XrBodySkeletonHTC(_) \
+ _(type) \
+ _(next) \
+ _(jointCount) \
+ _(joints) \
+
/// Calls your macro with the name of each member of XrActiveActionSetPriorityEXT, in order.
#define XR_LIST_STRUCT_XrActiveActionSetPriorityEXT(_) \
_(actionSet) \
@@ -4739,6 +5056,114 @@ XR_ENUM_STR(XrResult);
_(next) \
_(enabled) \
+/// Calls your macro with the name of each member of XrSystemNotificationsSetInfoML, in order.
+#define XR_LIST_STRUCT_XrSystemNotificationsSetInfoML(_) \
+ _(type) \
+ _(next) \
+ _(suppressNotifications) \
+
+/// Calls your macro with the name of each member of XrWorldMeshDetectorCreateInfoML, in order.
+#define XR_LIST_STRUCT_XrWorldMeshDetectorCreateInfoML(_) \
+ _(type) \
+ _(next) \
+
+/// Calls your macro with the name of each member of XrWorldMeshBlockStateML, in order.
+#define XR_LIST_STRUCT_XrWorldMeshBlockStateML(_) \
+ _(type) \
+ _(next) \
+ _(uuid) \
+ _(meshBoundingBoxCenter) \
+ _(meshBoundingBoxExtents) \
+ _(lastUpdateTime) \
+ _(status) \
+
+/// Calls your macro with the name of each member of XrWorldMeshStateRequestInfoML, in order.
+#define XR_LIST_STRUCT_XrWorldMeshStateRequestInfoML(_) \
+ _(type) \
+ _(next) \
+ _(baseSpace) \
+ _(time) \
+ _(boundingBoxCenter) \
+ _(boundingBoxExtents) \
+
+/// Calls your macro with the name of each member of XrWorldMeshStateRequestCompletionML, in order.
+#define XR_LIST_STRUCT_XrWorldMeshStateRequestCompletionML(_) \
+ _(type) \
+ _(next) \
+ _(futureResult) \
+ _(timestamp) \
+ _(meshBlockStateCapacityInput) \
+ _(meshBlockStateCountOutput) \
+ _(meshBlockStates) \
+
+/// Calls your macro with the name of each member of XrWorldMeshBufferRecommendedSizeInfoML, in order.
+#define XR_LIST_STRUCT_XrWorldMeshBufferRecommendedSizeInfoML(_) \
+ _(type) \
+ _(next) \
+ _(maxBlockCount) \
+
+/// Calls your macro with the name of each member of XrWorldMeshBufferSizeML, in order.
+#define XR_LIST_STRUCT_XrWorldMeshBufferSizeML(_) \
+ _(type) \
+ _(next) \
+ _(size) \
+
+/// Calls your macro with the name of each member of XrWorldMeshBufferML, in order.
+#define XR_LIST_STRUCT_XrWorldMeshBufferML(_) \
+ _(type) \
+ _(next) \
+ _(bufferSize) \
+ _(buffer) \
+
+/// Calls your macro with the name of each member of XrWorldMeshBlockRequestML, in order.
+#define XR_LIST_STRUCT_XrWorldMeshBlockRequestML(_) \
+ _(type) \
+ _(next) \
+ _(uuid) \
+ _(lod) \
+
+/// Calls your macro with the name of each member of XrWorldMeshGetInfoML, in order.
+#define XR_LIST_STRUCT_XrWorldMeshGetInfoML(_) \
+ _(type) \
+ _(next) \
+ _(flags) \
+ _(fillHoleLength) \
+ _(disconnectedComponentArea) \
+ _(blockCount) \
+ _(blocks) \
+
+/// Calls your macro with the name of each member of XrWorldMeshBlockML, in order.
+#define XR_LIST_STRUCT_XrWorldMeshBlockML(_) \
+ _(type) \
+ _(next) \
+ _(uuid) \
+ _(blockResult) \
+ _(lod) \
+ _(flags) \
+ _(indexCount) \
+ _(indexBuffer) \
+ _(vertexCount) \
+ _(vertexBuffer) \
+ _(normalCount) \
+ _(normalBuffer) \
+ _(confidenceCount) \
+ _(confidenceBuffer) \
+
+/// Calls your macro with the name of each member of XrWorldMeshRequestCompletionInfoML, in order.
+#define XR_LIST_STRUCT_XrWorldMeshRequestCompletionInfoML(_) \
+ _(type) \
+ _(next) \
+ _(meshSpace) \
+ _(meshSpaceLocateTime) \
+
+/// Calls your macro with the name of each member of XrWorldMeshRequestCompletionML, in order.
+#define XR_LIST_STRUCT_XrWorldMeshRequestCompletionML(_) \
+ _(type) \
+ _(next) \
+ _(futureResult) \
+ _(blockCount) \
+ _(blocks) \
+
/// Calls your macro with the structure type name and the XrStructureType constant for
@@ -4747,6 +5172,7 @@ XR_ENUM_STR(XrResult);
XR_LIST_STRUCTURE_TYPES_CORE(_) \
XR_LIST_STRUCTURE_TYPES_XR_USE_GRAPHICS_API_D3D11(_) \
XR_LIST_STRUCTURE_TYPES_XR_USE_GRAPHICS_API_D3D12(_) \
+ XR_LIST_STRUCTURE_TYPES_XR_USE_GRAPHICS_API_METAL(_) \
XR_LIST_STRUCTURE_TYPES_XR_USE_GRAPHICS_API_OPENGL(_) \
XR_LIST_STRUCTURE_TYPES_XR_USE_GRAPHICS_API_OPENGL_XR_USE_PLATFORM_WAYLAND(_) \
XR_LIST_STRUCTURE_TYPES_XR_USE_GRAPHICS_API_OPENGL_XR_USE_PLATFORM_WIN32(_) \
@@ -4962,6 +5388,22 @@ XR_ENUM_STR(XrResult);
_(XrMapLocalizationRequestInfoML, XR_TYPE_MAP_LOCALIZATION_REQUEST_INFO_ML) \
_(XrLocalizationMapImportInfoML, XR_TYPE_LOCALIZATION_MAP_IMPORT_INFO_ML) \
_(XrLocalizationEnableEventsInfoML, XR_TYPE_LOCALIZATION_ENABLE_EVENTS_INFO_ML) \
+ _(XrSpatialAnchorsCreateInfoFromPoseML, XR_TYPE_SPATIAL_ANCHORS_CREATE_INFO_FROM_POSE_ML) \
+ _(XrCreateSpatialAnchorsCompletionML, XR_TYPE_CREATE_SPATIAL_ANCHORS_COMPLETION_ML) \
+ _(XrSpatialAnchorStateML, XR_TYPE_SPATIAL_ANCHOR_STATE_ML) \
+ _(XrSpatialAnchorsCreateStorageInfoML, XR_TYPE_SPATIAL_ANCHORS_CREATE_STORAGE_INFO_ML) \
+ _(XrSpatialAnchorsQueryInfoRadiusML, XR_TYPE_SPATIAL_ANCHORS_QUERY_INFO_RADIUS_ML) \
+ _(XrSpatialAnchorsQueryCompletionML, XR_TYPE_SPATIAL_ANCHORS_QUERY_COMPLETION_ML) \
+ _(XrSpatialAnchorsCreateInfoFromUuidsML, XR_TYPE_SPATIAL_ANCHORS_CREATE_INFO_FROM_UUIDS_ML) \
+ _(XrSpatialAnchorsPublishInfoML, XR_TYPE_SPATIAL_ANCHORS_PUBLISH_INFO_ML) \
+ _(XrSpatialAnchorsPublishCompletionML, XR_TYPE_SPATIAL_ANCHORS_PUBLISH_COMPLETION_ML) \
+ _(XrSpatialAnchorsDeleteInfoML, XR_TYPE_SPATIAL_ANCHORS_DELETE_INFO_ML) \
+ _(XrSpatialAnchorsDeleteCompletionML, XR_TYPE_SPATIAL_ANCHORS_DELETE_COMPLETION_ML) \
+ _(XrSpatialAnchorsUpdateExpirationInfoML, XR_TYPE_SPATIAL_ANCHORS_UPDATE_EXPIRATION_INFO_ML) \
+ _(XrSpatialAnchorsUpdateExpirationCompletionML, XR_TYPE_SPATIAL_ANCHORS_UPDATE_EXPIRATION_COMPLETION_ML) \
+ _(XrSpatialAnchorsPublishCompletionDetailsML, XR_TYPE_SPATIAL_ANCHORS_PUBLISH_COMPLETION_DETAILS_ML) \
+ _(XrSpatialAnchorsDeleteCompletionDetailsML, XR_TYPE_SPATIAL_ANCHORS_DELETE_COMPLETION_DETAILS_ML) \
+ _(XrSpatialAnchorsUpdateExpirationCompletionDetailsML, XR_TYPE_SPATIAL_ANCHORS_UPDATE_EXPIRATION_COMPLETION_DETAILS_ML) \
_(XrSpatialAnchorPersistenceInfoMSFT, XR_TYPE_SPATIAL_ANCHOR_PERSISTENCE_INFO_MSFT) \
_(XrSpatialAnchorFromPersistedAnchorCreateInfoMSFT, XR_TYPE_SPATIAL_ANCHOR_FROM_PERSISTED_ANCHOR_CREATE_INFO_MSFT) \
_(XrSceneMarkersMSFT, XR_TYPE_SCENE_MARKERS_MSFT) \
@@ -5061,6 +5503,11 @@ XR_ENUM_STR(XrResult);
_(XrFoveationCustomModeInfoHTC, XR_TYPE_FOVEATION_CUSTOM_MODE_INFO_HTC) \
_(XrSystemAnchorPropertiesHTC, XR_TYPE_SYSTEM_ANCHOR_PROPERTIES_HTC) \
_(XrSpatialAnchorCreateInfoHTC, XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_HTC) \
+ _(XrSystemBodyTrackingPropertiesHTC, XR_TYPE_SYSTEM_BODY_TRACKING_PROPERTIES_HTC) \
+ _(XrBodyTrackerCreateInfoHTC, XR_TYPE_BODY_TRACKER_CREATE_INFO_HTC) \
+ _(XrBodyJointsLocateInfoHTC, XR_TYPE_BODY_JOINTS_LOCATE_INFO_HTC) \
+ _(XrBodyJointLocationsHTC, XR_TYPE_BODY_JOINT_LOCATIONS_HTC) \
+ _(XrBodySkeletonHTC, XR_TYPE_BODY_SKELETON_HTC) \
_(XrActiveActionSetPrioritiesEXT, XR_TYPE_ACTIVE_ACTION_SET_PRIORITIES_EXT) \
_(XrSystemForceFeedbackCurlPropertiesMNDX, XR_TYPE_SYSTEM_FORCE_FEEDBACK_CURL_PROPERTIES_MNDX) \
_(XrForceFeedbackCurlApplyLocationsMNDX, XR_TYPE_FORCE_FEEDBACK_CURL_APPLY_LOCATIONS_MNDX) \
@@ -5082,6 +5529,19 @@ XR_ENUM_STR(XrResult);
_(XrEventDataHeadsetFitChangedML, XR_TYPE_EVENT_DATA_HEADSET_FIT_CHANGED_ML) \
_(XrEventDataEyeCalibrationChangedML, XR_TYPE_EVENT_DATA_EYE_CALIBRATION_CHANGED_ML) \
_(XrUserCalibrationEnableEventsInfoML, XR_TYPE_USER_CALIBRATION_ENABLE_EVENTS_INFO_ML) \
+ _(XrSystemNotificationsSetInfoML, XR_TYPE_SYSTEM_NOTIFICATIONS_SET_INFO_ML) \
+ _(XrWorldMeshDetectorCreateInfoML, XR_TYPE_WORLD_MESH_DETECTOR_CREATE_INFO_ML) \
+ _(XrWorldMeshBlockStateML, XR_TYPE_WORLD_MESH_BLOCK_STATE_ML) \
+ _(XrWorldMeshStateRequestInfoML, XR_TYPE_WORLD_MESH_STATE_REQUEST_INFO_ML) \
+ _(XrWorldMeshStateRequestCompletionML, XR_TYPE_WORLD_MESH_STATE_REQUEST_COMPLETION_ML) \
+ _(XrWorldMeshBufferRecommendedSizeInfoML, XR_TYPE_WORLD_MESH_BUFFER_RECOMMENDED_SIZE_INFO_ML) \
+ _(XrWorldMeshBufferSizeML, XR_TYPE_WORLD_MESH_BUFFER_SIZE_ML) \
+ _(XrWorldMeshBufferML, XR_TYPE_WORLD_MESH_BUFFER_ML) \
+ _(XrWorldMeshBlockRequestML, XR_TYPE_WORLD_MESH_BLOCK_REQUEST_ML) \
+ _(XrWorldMeshGetInfoML, XR_TYPE_WORLD_MESH_GET_INFO_ML) \
+ _(XrWorldMeshBlockML, XR_TYPE_WORLD_MESH_BLOCK_ML) \
+ _(XrWorldMeshRequestCompletionInfoML, XR_TYPE_WORLD_MESH_REQUEST_COMPLETION_INFO_ML) \
+ _(XrWorldMeshRequestCompletionML, XR_TYPE_WORLD_MESH_REQUEST_COMPLETION_ML) \
#if defined(XR_USE_GRAPHICS_API_D3D11)
@@ -5108,6 +5568,18 @@ XR_ENUM_STR(XrResult);
#define XR_LIST_STRUCTURE_TYPES_XR_USE_GRAPHICS_API_D3D12(_)
#endif
+#if defined(XR_USE_GRAPHICS_API_METAL)
+/// Implementation detail of XR_LIST_STRUCTURE_TYPES()
+/// Structure types available only when XR_USE_GRAPHICS_API_METAL is defined
+#define XR_LIST_STRUCTURE_TYPES_XR_USE_GRAPHICS_API_METAL(_) \
+ _(XrGraphicsBindingMetalKHR, XR_TYPE_GRAPHICS_BINDING_METAL_KHR) \
+ _(XrSwapchainImageMetalKHR, XR_TYPE_SWAPCHAIN_IMAGE_METAL_KHR) \
+ _(XrGraphicsRequirementsMetalKHR, XR_TYPE_GRAPHICS_REQUIREMENTS_METAL_KHR) \
+
+#else
+#define XR_LIST_STRUCTURE_TYPES_XR_USE_GRAPHICS_API_METAL(_)
+#endif
+
#if defined(XR_USE_GRAPHICS_API_OPENGL)
/// Implementation detail of XR_LIST_STRUCTURE_TYPES()
/// Structure types available only when XR_USE_GRAPHICS_API_OPENGL is defined
@@ -5264,6 +5736,7 @@ XR_ENUM_STR(XrResult);
_(XR_KHR_vulkan_enable, 26) \
_(XR_KHR_D3D11_enable, 28) \
_(XR_KHR_D3D12_enable, 29) \
+ _(XR_KHR_metal_enable, 30) \
_(XR_EXT_eye_gaze_interaction, 31) \
_(XR_KHR_visibility_mask, 32) \
_(XR_EXTX_overlay, 34) \
@@ -5340,6 +5813,8 @@ XR_ENUM_STR(XrResult);
_(XR_ML_compat, 138) \
_(XR_ML_marker_understanding, 139) \
_(XR_ML_localization_map, 140) \
+ _(XR_ML_spatial_anchors, 141) \
+ _(XR_ML_spatial_anchors_storage, 142) \
_(XR_MSFT_spatial_anchor_persistence, 143) \
_(XR_MSFT_scene_marker, 148) \
_(XR_ULTRALEAP_hand_tracking_forearm, 150) \
@@ -5390,6 +5865,7 @@ XR_ENUM_STR(XrResult);
_(XR_HTC_passthrough, 318) \
_(XR_HTC_foveation, 319) \
_(XR_HTC_anchor, 320) \
+ _(XR_HTC_body_tracking, 321) \
_(XR_EXT_active_action_set_priority, 374) \
_(XR_MNDX_force_feedback_curl, 376) \
_(XR_BD_controller_interaction, 385) \
@@ -5401,6 +5877,9 @@ XR_ENUM_STR(XrResult);
_(XR_EXT_user_presence, 471) \
_(XR_KHR_locate_spaces, 472) \
_(XR_ML_user_calibration, 473) \
+ _(XR_ML_system_notifications, 474) \
+ _(XR_ML_world_mesh_detection, 475) \
+ _(XR_ML_view_configuration_depth_range_change, 484) \
_(XR_YVR_controller_interaction, 498) \
_(XR_EXT_composition_layer_inverted_alpha, 555) \
_(XR_KHR_maintenance1, 711) \
@@ -5546,6 +6025,14 @@ XR_ENUM_STR(XrResult);
_(GetD3D12GraphicsRequirementsKHR, KHR_D3D12_enable) \
+/// For every function defined by XR_KHR_metal_enable in this version of the spec,
+/// calls your macro with the function name and extension name.
+/// Trims the leading `xr` from the function name and the leading `XR_` from the feature name,
+/// because it is easy to add back but impossible to remove with the preprocessor.
+#define XR_LIST_FUNCTIONS_XR_KHR_metal_enable(_) \
+ _(GetMetalGraphicsRequirementsKHR, KHR_metal_enable) \
+
+
/// For every function defined by XR_KHR_visibility_mask in this version of the spec,
/// calls your macro with the function name and extension name.
/// Trims the leading `xr` from the function name and the leading `XR_` from the feature name,
@@ -5932,6 +6419,33 @@ XR_ENUM_STR(XrResult);
_(GetExportedLocalizationMapDataML, ML_localization_map) \
+/// For every function defined by XR_ML_spatial_anchors in this version of the spec,
+/// calls your macro with the function name and extension name.
+/// Trims the leading `xr` from the function name and the leading `XR_` from the feature name,
+/// because it is easy to add back but impossible to remove with the preprocessor.
+#define XR_LIST_FUNCTIONS_XR_ML_spatial_anchors(_) \
+ _(CreateSpatialAnchorsAsyncML, ML_spatial_anchors) \
+ _(CreateSpatialAnchorsCompleteML, ML_spatial_anchors) \
+ _(GetSpatialAnchorStateML, ML_spatial_anchors) \
+
+
+/// For every function defined by XR_ML_spatial_anchors_storage in this version of the spec,
+/// calls your macro with the function name and extension name.
+/// Trims the leading `xr` from the function name and the leading `XR_` from the feature name,
+/// because it is easy to add back but impossible to remove with the preprocessor.
+#define XR_LIST_FUNCTIONS_XR_ML_spatial_anchors_storage(_) \
+ _(CreateSpatialAnchorsStorageML, ML_spatial_anchors_storage) \
+ _(DestroySpatialAnchorsStorageML, ML_spatial_anchors_storage) \
+ _(QuerySpatialAnchorsAsyncML, ML_spatial_anchors_storage) \
+ _(QuerySpatialAnchorsCompleteML, ML_spatial_anchors_storage) \
+ _(PublishSpatialAnchorsAsyncML, ML_spatial_anchors_storage) \
+ _(PublishSpatialAnchorsCompleteML, ML_spatial_anchors_storage) \
+ _(DeleteSpatialAnchorsAsyncML, ML_spatial_anchors_storage) \
+ _(DeleteSpatialAnchorsCompleteML, ML_spatial_anchors_storage) \
+ _(UpdateSpatialAnchorsExpirationAsyncML, ML_spatial_anchors_storage) \
+ _(UpdateSpatialAnchorsExpirationCompleteML, ML_spatial_anchors_storage) \
+
+
/// For every function defined by XR_MSFT_spatial_anchor_persistence in this version of the spec,
/// calls your macro with the function name and extension name.
/// Trims the leading `xr` from the function name and the leading `XR_` from the feature name,
@@ -6220,6 +6734,17 @@ XR_ENUM_STR(XrResult);
_(GetSpatialAnchorNameHTC, HTC_anchor) \
+/// For every function defined by XR_HTC_body_tracking in this version of the spec,
+/// calls your macro with the function name and extension name.
+/// Trims the leading `xr` from the function name and the leading `XR_` from the feature name,
+/// because it is easy to add back but impossible to remove with the preprocessor.
+#define XR_LIST_FUNCTIONS_XR_HTC_body_tracking(_) \
+ _(CreateBodyTrackerHTC, HTC_body_tracking) \
+ _(DestroyBodyTrackerHTC, HTC_body_tracking) \
+ _(LocateBodyJointsHTC, HTC_body_tracking) \
+ _(GetBodySkeletonHTC, HTC_body_tracking) \
+
+
/// For every function defined by XR_MNDX_force_feedback_curl in this version of the spec,
/// calls your macro with the function name and extension name.
/// Trims the leading `xr` from the function name and the leading `XR_` from the feature name,
@@ -6258,6 +6783,30 @@ XR_ENUM_STR(XrResult);
_(EnableUserCalibrationEventsML, ML_user_calibration) \
+/// For every function defined by XR_ML_system_notifications in this version of the spec,
+/// calls your macro with the function name and extension name.
+/// Trims the leading `xr` from the function name and the leading `XR_` from the feature name,
+/// because it is easy to add back but impossible to remove with the preprocessor.
+#define XR_LIST_FUNCTIONS_XR_ML_system_notifications(_) \
+ _(SetSystemNotificationsML, ML_system_notifications) \
+
+
+/// For every function defined by XR_ML_world_mesh_detection in this version of the spec,
+/// calls your macro with the function name and extension name.
+/// Trims the leading `xr` from the function name and the leading `XR_` from the feature name,
+/// because it is easy to add back but impossible to remove with the preprocessor.
+#define XR_LIST_FUNCTIONS_XR_ML_world_mesh_detection(_) \
+ _(CreateWorldMeshDetectorML, ML_world_mesh_detection) \
+ _(DestroyWorldMeshDetectorML, ML_world_mesh_detection) \
+ _(RequestWorldMeshStateAsyncML, ML_world_mesh_detection) \
+ _(RequestWorldMeshStateCompleteML, ML_world_mesh_detection) \
+ _(GetWorldMeshBufferRecommendSizeML, ML_world_mesh_detection) \
+ _(AllocateWorldMeshBufferML, ML_world_mesh_detection) \
+ _(FreeWorldMeshBufferML, ML_world_mesh_detection) \
+ _(RequestWorldMeshAsyncML, ML_world_mesh_detection) \
+ _(RequestWorldMeshCompleteML, ML_world_mesh_detection) \
+
+
#endif
diff --git a/thirdparty/openxr/include/openxr/openxr_reflection_parent_structs.h b/thirdparty/openxr/include/openxr/openxr_reflection_parent_structs.h
index 50466abc1b..bffab47cc5 100644
--- a/thirdparty/openxr/include/openxr/openxr_reflection_parent_structs.h
+++ b/thirdparty/openxr/include/openxr/openxr_reflection_parent_structs.h
@@ -238,6 +238,53 @@ This file contains expansion macros (X Macros) for OpenXR structures that have a
+/// Like XR_LIST_ALL_STRUCTURE_TYPES, but only includes types whose parent struct type is XrSpatialAnchorsCreateInfoBaseHeaderML
+#define XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrSpatialAnchorsCreateInfoBaseHeaderML(_avail, _unavail) \
+ _impl_XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrSpatialAnchorsCreateInfoBaseHeaderML_CORE(_avail, _unavail) \
+
+
+// Implementation detail of XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrSpatialAnchorsCreateInfoBaseHeaderML()
+#define _impl_XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrSpatialAnchorsCreateInfoBaseHeaderML_CORE(_avail, _unavail) \
+ _avail(XrSpatialAnchorsCreateInfoFromPoseML, XR_TYPE_SPATIAL_ANCHORS_CREATE_INFO_FROM_POSE_ML) \
+ _avail(XrSpatialAnchorsCreateInfoFromUuidsML, XR_TYPE_SPATIAL_ANCHORS_CREATE_INFO_FROM_UUIDS_ML) \
+
+
+
+
+
+/// Like XR_LIST_ALL_STRUCTURE_TYPES, but only includes types whose parent struct type is XrFutureCompletionBaseHeaderEXT
+#define XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrFutureCompletionBaseHeaderEXT(_avail, _unavail) \
+ _impl_XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrFutureCompletionBaseHeaderEXT_CORE(_avail, _unavail) \
+
+
+// Implementation detail of XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrFutureCompletionBaseHeaderEXT()
+#define _impl_XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrFutureCompletionBaseHeaderEXT_CORE(_avail, _unavail) \
+ _avail(XrCreateSpatialAnchorsCompletionML, XR_TYPE_CREATE_SPATIAL_ANCHORS_COMPLETION_ML) \
+ _avail(XrSpatialAnchorsQueryCompletionML, XR_TYPE_SPATIAL_ANCHORS_QUERY_COMPLETION_ML) \
+ _avail(XrSpatialAnchorsPublishCompletionML, XR_TYPE_SPATIAL_ANCHORS_PUBLISH_COMPLETION_ML) \
+ _avail(XrSpatialAnchorsDeleteCompletionML, XR_TYPE_SPATIAL_ANCHORS_DELETE_COMPLETION_ML) \
+ _avail(XrSpatialAnchorsUpdateExpirationCompletionML, XR_TYPE_SPATIAL_ANCHORS_UPDATE_EXPIRATION_COMPLETION_ML) \
+ _avail(XrFutureCompletionEXT, XR_TYPE_FUTURE_COMPLETION_EXT) \
+ _avail(XrWorldMeshStateRequestCompletionML, XR_TYPE_WORLD_MESH_STATE_REQUEST_COMPLETION_ML) \
+ _avail(XrWorldMeshRequestCompletionML, XR_TYPE_WORLD_MESH_REQUEST_COMPLETION_ML) \
+
+
+
+
+
+/// Like XR_LIST_ALL_STRUCTURE_TYPES, but only includes types whose parent struct type is XrSpatialAnchorsQueryInfoBaseHeaderML
+#define XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrSpatialAnchorsQueryInfoBaseHeaderML(_avail, _unavail) \
+ _impl_XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrSpatialAnchorsQueryInfoBaseHeaderML_CORE(_avail, _unavail) \
+
+
+// Implementation detail of XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrSpatialAnchorsQueryInfoBaseHeaderML()
+#define _impl_XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrSpatialAnchorsQueryInfoBaseHeaderML_CORE(_avail, _unavail) \
+ _avail(XrSpatialAnchorsQueryInfoRadiusML, XR_TYPE_SPATIAL_ANCHORS_QUERY_INFO_RADIUS_ML) \
+
+
+
+
+
/// Like XR_LIST_ALL_STRUCTURE_TYPES, but only includes types whose parent struct type is XrSpaceQueryInfoBaseHeaderFB
#define XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrSpaceQueryInfoBaseHeaderFB(_avail, _unavail) \
_impl_XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrSpaceQueryInfoBaseHeaderFB_CORE(_avail, _unavail) \
@@ -265,18 +312,5 @@ This file contains expansion macros (X Macros) for OpenXR structures that have a
-/// Like XR_LIST_ALL_STRUCTURE_TYPES, but only includes types whose parent struct type is XrFutureCompletionBaseHeaderEXT
-#define XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrFutureCompletionBaseHeaderEXT(_avail, _unavail) \
- _impl_XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrFutureCompletionBaseHeaderEXT_CORE(_avail, _unavail) \
-
-
-// Implementation detail of XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrFutureCompletionBaseHeaderEXT()
-#define _impl_XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrFutureCompletionBaseHeaderEXT_CORE(_avail, _unavail) \
- _avail(XrFutureCompletionEXT, XR_TYPE_FUTURE_COMPLETION_EXT) \
-
-
-
-
-
#endif
diff --git a/thirdparty/openxr/include/openxr/openxr_reflection_structs.h b/thirdparty/openxr/include/openxr/openxr_reflection_structs.h
index e87e55b560..b1ab8e7779 100644
--- a/thirdparty/openxr/include/openxr/openxr_reflection_structs.h
+++ b/thirdparty/openxr/include/openxr/openxr_reflection_structs.h
@@ -27,6 +27,7 @@ This file contains expansion macros (X Macros) for OpenXR structures.
_impl_XR_LIST_ALL_STRUCTURE_TYPES_CORE(_avail, _unavail) \
_impl_XR_LIST_ALL_STRUCTURE_TYPES_XR_USE_GRAPHICS_API_D3D11(_avail, _unavail) \
_impl_XR_LIST_ALL_STRUCTURE_TYPES_XR_USE_GRAPHICS_API_D3D12(_avail, _unavail) \
+ _impl_XR_LIST_ALL_STRUCTURE_TYPES_XR_USE_GRAPHICS_API_METAL(_avail, _unavail) \
_impl_XR_LIST_ALL_STRUCTURE_TYPES_XR_USE_GRAPHICS_API_OPENGL(_avail, _unavail) \
_impl_XR_LIST_ALL_STRUCTURE_TYPES_XR_USE_GRAPHICS_API_OPENGL_XR_USE_PLATFORM_WAYLAND(_avail, _unavail) \
_impl_XR_LIST_ALL_STRUCTURE_TYPES_XR_USE_GRAPHICS_API_OPENGL_XR_USE_PLATFORM_WIN32(_avail, _unavail) \
@@ -242,6 +243,22 @@ This file contains expansion macros (X Macros) for OpenXR structures.
_avail(XrMapLocalizationRequestInfoML, XR_TYPE_MAP_LOCALIZATION_REQUEST_INFO_ML) \
_avail(XrLocalizationMapImportInfoML, XR_TYPE_LOCALIZATION_MAP_IMPORT_INFO_ML) \
_avail(XrLocalizationEnableEventsInfoML, XR_TYPE_LOCALIZATION_ENABLE_EVENTS_INFO_ML) \
+ _avail(XrSpatialAnchorsCreateInfoFromPoseML, XR_TYPE_SPATIAL_ANCHORS_CREATE_INFO_FROM_POSE_ML) \
+ _avail(XrCreateSpatialAnchorsCompletionML, XR_TYPE_CREATE_SPATIAL_ANCHORS_COMPLETION_ML) \
+ _avail(XrSpatialAnchorStateML, XR_TYPE_SPATIAL_ANCHOR_STATE_ML) \
+ _avail(XrSpatialAnchorsCreateStorageInfoML, XR_TYPE_SPATIAL_ANCHORS_CREATE_STORAGE_INFO_ML) \
+ _avail(XrSpatialAnchorsQueryInfoRadiusML, XR_TYPE_SPATIAL_ANCHORS_QUERY_INFO_RADIUS_ML) \
+ _avail(XrSpatialAnchorsQueryCompletionML, XR_TYPE_SPATIAL_ANCHORS_QUERY_COMPLETION_ML) \
+ _avail(XrSpatialAnchorsCreateInfoFromUuidsML, XR_TYPE_SPATIAL_ANCHORS_CREATE_INFO_FROM_UUIDS_ML) \
+ _avail(XrSpatialAnchorsPublishInfoML, XR_TYPE_SPATIAL_ANCHORS_PUBLISH_INFO_ML) \
+ _avail(XrSpatialAnchorsPublishCompletionML, XR_TYPE_SPATIAL_ANCHORS_PUBLISH_COMPLETION_ML) \
+ _avail(XrSpatialAnchorsDeleteInfoML, XR_TYPE_SPATIAL_ANCHORS_DELETE_INFO_ML) \
+ _avail(XrSpatialAnchorsDeleteCompletionML, XR_TYPE_SPATIAL_ANCHORS_DELETE_COMPLETION_ML) \
+ _avail(XrSpatialAnchorsUpdateExpirationInfoML, XR_TYPE_SPATIAL_ANCHORS_UPDATE_EXPIRATION_INFO_ML) \
+ _avail(XrSpatialAnchorsUpdateExpirationCompletionML, XR_TYPE_SPATIAL_ANCHORS_UPDATE_EXPIRATION_COMPLETION_ML) \
+ _avail(XrSpatialAnchorsPublishCompletionDetailsML, XR_TYPE_SPATIAL_ANCHORS_PUBLISH_COMPLETION_DETAILS_ML) \
+ _avail(XrSpatialAnchorsDeleteCompletionDetailsML, XR_TYPE_SPATIAL_ANCHORS_DELETE_COMPLETION_DETAILS_ML) \
+ _avail(XrSpatialAnchorsUpdateExpirationCompletionDetailsML, XR_TYPE_SPATIAL_ANCHORS_UPDATE_EXPIRATION_COMPLETION_DETAILS_ML) \
_avail(XrSpatialAnchorPersistenceInfoMSFT, XR_TYPE_SPATIAL_ANCHOR_PERSISTENCE_INFO_MSFT) \
_avail(XrSpatialAnchorFromPersistedAnchorCreateInfoMSFT, XR_TYPE_SPATIAL_ANCHOR_FROM_PERSISTED_ANCHOR_CREATE_INFO_MSFT) \
_avail(XrSceneMarkersMSFT, XR_TYPE_SCENE_MARKERS_MSFT) \
@@ -341,6 +358,11 @@ This file contains expansion macros (X Macros) for OpenXR structures.
_avail(XrFoveationCustomModeInfoHTC, XR_TYPE_FOVEATION_CUSTOM_MODE_INFO_HTC) \
_avail(XrSystemAnchorPropertiesHTC, XR_TYPE_SYSTEM_ANCHOR_PROPERTIES_HTC) \
_avail(XrSpatialAnchorCreateInfoHTC, XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_HTC) \
+ _avail(XrSystemBodyTrackingPropertiesHTC, XR_TYPE_SYSTEM_BODY_TRACKING_PROPERTIES_HTC) \
+ _avail(XrBodyTrackerCreateInfoHTC, XR_TYPE_BODY_TRACKER_CREATE_INFO_HTC) \
+ _avail(XrBodyJointsLocateInfoHTC, XR_TYPE_BODY_JOINTS_LOCATE_INFO_HTC) \
+ _avail(XrBodyJointLocationsHTC, XR_TYPE_BODY_JOINT_LOCATIONS_HTC) \
+ _avail(XrBodySkeletonHTC, XR_TYPE_BODY_SKELETON_HTC) \
_avail(XrActiveActionSetPrioritiesEXT, XR_TYPE_ACTIVE_ACTION_SET_PRIORITIES_EXT) \
_avail(XrSystemForceFeedbackCurlPropertiesMNDX, XR_TYPE_SYSTEM_FORCE_FEEDBACK_CURL_PROPERTIES_MNDX) \
_avail(XrForceFeedbackCurlApplyLocationsMNDX, XR_TYPE_FORCE_FEEDBACK_CURL_APPLY_LOCATIONS_MNDX) \
@@ -362,6 +384,19 @@ This file contains expansion macros (X Macros) for OpenXR structures.
_avail(XrEventDataHeadsetFitChangedML, XR_TYPE_EVENT_DATA_HEADSET_FIT_CHANGED_ML) \
_avail(XrEventDataEyeCalibrationChangedML, XR_TYPE_EVENT_DATA_EYE_CALIBRATION_CHANGED_ML) \
_avail(XrUserCalibrationEnableEventsInfoML, XR_TYPE_USER_CALIBRATION_ENABLE_EVENTS_INFO_ML) \
+ _avail(XrSystemNotificationsSetInfoML, XR_TYPE_SYSTEM_NOTIFICATIONS_SET_INFO_ML) \
+ _avail(XrWorldMeshDetectorCreateInfoML, XR_TYPE_WORLD_MESH_DETECTOR_CREATE_INFO_ML) \
+ _avail(XrWorldMeshBlockStateML, XR_TYPE_WORLD_MESH_BLOCK_STATE_ML) \
+ _avail(XrWorldMeshStateRequestInfoML, XR_TYPE_WORLD_MESH_STATE_REQUEST_INFO_ML) \
+ _avail(XrWorldMeshStateRequestCompletionML, XR_TYPE_WORLD_MESH_STATE_REQUEST_COMPLETION_ML) \
+ _avail(XrWorldMeshBufferRecommendedSizeInfoML, XR_TYPE_WORLD_MESH_BUFFER_RECOMMENDED_SIZE_INFO_ML) \
+ _avail(XrWorldMeshBufferSizeML, XR_TYPE_WORLD_MESH_BUFFER_SIZE_ML) \
+ _avail(XrWorldMeshBufferML, XR_TYPE_WORLD_MESH_BUFFER_ML) \
+ _avail(XrWorldMeshBlockRequestML, XR_TYPE_WORLD_MESH_BLOCK_REQUEST_ML) \
+ _avail(XrWorldMeshGetInfoML, XR_TYPE_WORLD_MESH_GET_INFO_ML) \
+ _avail(XrWorldMeshBlockML, XR_TYPE_WORLD_MESH_BLOCK_ML) \
+ _avail(XrWorldMeshRequestCompletionInfoML, XR_TYPE_WORLD_MESH_REQUEST_COMPLETION_INFO_ML) \
+ _avail(XrWorldMeshRequestCompletionML, XR_TYPE_WORLD_MESH_REQUEST_COMPLETION_ML) \
#if defined(XR_USE_GRAPHICS_API_D3D11)
@@ -392,6 +427,20 @@ This file contains expansion macros (X Macros) for OpenXR structures.
#endif
+#if defined(XR_USE_GRAPHICS_API_METAL)
+#define _impl_XR_LIST_ALL_STRUCTURE_TYPES_XR_USE_GRAPHICS_API_METAL(_avail, _unavail) \
+ _avail(XrGraphicsBindingMetalKHR, XR_TYPE_GRAPHICS_BINDING_METAL_KHR) \
+ _avail(XrSwapchainImageMetalKHR, XR_TYPE_SWAPCHAIN_IMAGE_METAL_KHR) \
+ _avail(XrGraphicsRequirementsMetalKHR, XR_TYPE_GRAPHICS_REQUIREMENTS_METAL_KHR) \
+
+#else
+#define _impl_XR_LIST_ALL_STRUCTURE_TYPES_XR_USE_GRAPHICS_API_METAL(_avail, _unavail) \
+ _unavail(XrGraphicsBindingMetalKHR, XR_TYPE_GRAPHICS_BINDING_METAL_KHR) \
+ _unavail(XrSwapchainImageMetalKHR, XR_TYPE_SWAPCHAIN_IMAGE_METAL_KHR) \
+ _unavail(XrGraphicsRequirementsMetalKHR, XR_TYPE_GRAPHICS_REQUIREMENTS_METAL_KHR) \
+
+#endif
+
#if defined(XR_USE_GRAPHICS_API_OPENGL)
#define _impl_XR_LIST_ALL_STRUCTURE_TYPES_XR_USE_GRAPHICS_API_OPENGL(_avail, _unavail) \
_avail(XrSwapchainImageOpenGLKHR, XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_KHR) \
diff --git a/thirdparty/openxr/src/common/platform_utils.hpp b/thirdparty/openxr/src/common/platform_utils.hpp
index d047c17d9d..19cd41cbb2 100644
--- a/thirdparty/openxr/src/common/platform_utils.hpp
+++ b/thirdparty/openxr/src/common/platform_utils.hpp
@@ -91,21 +91,22 @@ namespace detail {
static inline char* ImplGetEnv(const char* name) { return getenv(name); }
+// clang-format off
static inline char* ImplGetSecureEnv(const char* name) {
#ifdef HAVE_SECURE_GETENV
return secure_getenv(name);
#elif defined(HAVE___SECURE_GETENV)
return __secure_getenv(name);
#else
-// clang-format off
#pragma message( \
"Warning: Falling back to non-secure getenv for environmental" \
"lookups! Consider updating to a different libc.")
- // clang-format on
return ImplGetEnv(name);
#endif
}
+// clang-format on
+
} // namespace detail
#endif // defined(XR_OS_LINUX) || defined(XR_OS_APPLE)
diff --git a/thirdparty/openxr/src/common/xr_linear.h b/thirdparty/openxr/src/common/xr_linear.h
index 2b295ed304..cc525d3357 100644
--- a/thirdparty/openxr/src/common/xr_linear.h
+++ b/thirdparty/openxr/src/common/xr_linear.h
@@ -134,7 +134,7 @@ static const XrColor4f XrColorCyan = {0.0f, 1.0f, 1.0f, 1.0f};
static const XrColor4f XrColorLightGrey = {0.7f, 0.7f, 0.7f, 1.0f};
static const XrColor4f XrColorDarkGrey = {0.3f, 0.3f, 0.3f, 1.0f};
-typedef enum GraphicsAPI { GRAPHICS_VULKAN, GRAPHICS_OPENGL, GRAPHICS_OPENGL_ES, GRAPHICS_D3D } GraphicsAPI;
+typedef enum GraphicsAPI { GRAPHICS_VULKAN, GRAPHICS_OPENGL, GRAPHICS_OPENGL_ES, GRAPHICS_D3D, GRAPHICS_METAL } GraphicsAPI;
// Column-major, pre-multiplied. This type does not exist in the OpenXR API and is provided for convenience.
typedef struct XrMatrix4x4f {
diff --git a/thirdparty/openxr/src/loader/android_utilities.cpp b/thirdparty/openxr/src/loader/android_utilities.cpp
index 5c9b846b5a..1f7424b8e6 100644
--- a/thirdparty/openxr/src/loader/android_utilities.cpp
+++ b/thirdparty/openxr/src/loader/android_utilities.cpp
@@ -15,6 +15,7 @@
#include <openxr/openxr.h>
+#include <dlfcn.h>
#include <sstream>
#include <vector>
#include <android/log.h>
@@ -82,7 +83,6 @@ static Uri makeContentUri(bool systemBroker, int majorVersion, const char *abi)
.appendPath(abi)
.appendPath(RUNTIMES_PATH)
.appendPath(TABLE_PATH);
- ContentUris::appendId(builder, 0);
return builder.build();
}
@@ -175,7 +175,6 @@ static inline jni::Array<std::string> makeArray(std::initializer_list<const char
}
return ret;
}
-static constexpr auto TAG = "OpenXR-Loader";
#if defined(__arm__)
static constexpr auto ABI = "armeabi-v7l";
@@ -313,26 +312,41 @@ int getActiveRuntimeVirtualManifest(wrap::android::content::Context const &conte
cursor.moveToFirst();
- auto filename = cursor.getString(cursor.getColumnIndex(active_runtime::Columns::SO_FILENAME));
- auto libDir = cursor.getString(cursor.getColumnIndex(active_runtime::Columns::NATIVE_LIB_DIR));
- auto packageName = cursor.getString(cursor.getColumnIndex(active_runtime::Columns::PACKAGE_NAME));
-
- auto hasFunctions = cursor.getInt(cursor.getColumnIndex(active_runtime::Columns::HAS_FUNCTIONS)) == 1;
- __android_log_print(ANDROID_LOG_INFO, TAG, "Got runtime: package: %s, so filename: %s, native lib dir: %s, has functions: %s",
- packageName.c_str(), filename.c_str(), libDir.c_str(), (hasFunctions ? "yes" : "no"));
+ do {
+ auto filename = cursor.getString(cursor.getColumnIndex(active_runtime::Columns::SO_FILENAME));
+ auto libDir = cursor.getString(cursor.getColumnIndex(active_runtime::Columns::NATIVE_LIB_DIR));
+ auto packageName = cursor.getString(cursor.getColumnIndex(active_runtime::Columns::PACKAGE_NAME));
+
+ auto hasFunctions = cursor.getInt(cursor.getColumnIndex(active_runtime::Columns::HAS_FUNCTIONS)) == 1;
+ ALOGI("Got runtime: package: %s, so filename: %s, native lib dir: %s, has functions: %s", packageName.c_str(),
+ filename.c_str(), libDir.c_str(), (hasFunctions ? "yes" : "no"));
+
+ auto lib_path = libDir + "/" + filename;
+ auto *lib = dlopen(lib_path.c_str(), RTLD_LAZY | RTLD_LOCAL);
+ if (lib) {
+ // we found a runtime that we can dlopen, use it.
+ dlclose(lib);
+
+ JsonManifestBuilder builder{"runtime", lib_path};
+ if (hasFunctions) {
+ int result = populateFunctions(context, systemBroker, packageName, builder);
+ if (result != 0) {
+ ALOGW("Unable to populate functions from runtime: %s, checking for more records...", lib_path.c_str());
+ continue;
+ }
+ }
+ virtualManifest = builder.build();
+ cursor.close();
+ return 0;
+ }
+ // this runtime was not accessible, see if the broker has more runtimes on
+ // offer.
+ ALOGV("Unable to open broker provided runtime at %s, checking for more records...", lib_path.c_str());
+ } while (cursor.moveToNext());
- auto lib_path = libDir + "/" + filename;
+ ALOGE("Unable to open any of the broker provided runtimes.");
cursor.close();
-
- JsonManifestBuilder builder{"runtime", lib_path};
- if (hasFunctions) {
- int result = populateFunctions(context, systemBroker, packageName, builder);
- if (result != 0) {
- return result;
- }
- }
- virtualManifest = builder.build();
- return 0;
+ return -1;
}
} // namespace openxr_android
diff --git a/thirdparty/openxr/src/loader/loader_core.cpp b/thirdparty/openxr/src/loader/loader_core.cpp
index 8bf2609a08..a0d81b28dc 100644
--- a/thirdparty/openxr/src/loader/loader_core.cpp
+++ b/thirdparty/openxr/src/loader/loader_core.cpp
@@ -334,7 +334,7 @@ static XRAPI_ATTR XrResult XRAPI_CALL LoaderXrDestroyInstance(XrInstance instanc
return result;
}
- const std::unique_ptr<XrGeneratedDispatchTable> &dispatch_table = loader_instance->DispatchTable();
+ const std::unique_ptr<XrGeneratedDispatchTableCore> &dispatch_table = loader_instance->DispatchTable();
// If we allocated a default debug utils messenger, free it
XrDebugUtilsMessengerEXT messenger = loader_instance->DefaultDebugUtilsMessenger();
@@ -546,7 +546,7 @@ LoaderTrampolineSessionBeginDebugUtilsLabelRegionEXT(XrSession session, const Xr
return result;
}
LoaderLogger::GetInstance().BeginLabelRegion(session, labelInfo);
- const std::unique_ptr<XrGeneratedDispatchTable> &dispatch_table = loader_instance->DispatchTable();
+ const std::unique_ptr<XrGeneratedDispatchTableCore> &dispatch_table = loader_instance->DispatchTable();
if (nullptr != dispatch_table->SessionBeginDebugUtilsLabelRegionEXT) {
return dispatch_table->SessionBeginDebugUtilsLabelRegionEXT(session, labelInfo);
}
@@ -567,7 +567,7 @@ static XRAPI_ATTR XrResult XRAPI_CALL LoaderTrampolineSessionEndDebugUtilsLabelR
}
LoaderLogger::GetInstance().EndLabelRegion(session);
- const std::unique_ptr<XrGeneratedDispatchTable> &dispatch_table = loader_instance->DispatchTable();
+ const std::unique_ptr<XrGeneratedDispatchTableCore> &dispatch_table = loader_instance->DispatchTable();
if (nullptr != dispatch_table->SessionEndDebugUtilsLabelRegionEXT) {
return dispatch_table->SessionEndDebugUtilsLabelRegionEXT(session);
}
@@ -597,7 +597,7 @@ LoaderTrampolineSessionInsertDebugUtilsLabelEXT(XrSession session, const XrDebug
LoaderLogger::GetInstance().InsertLabel(session, labelInfo);
- const std::unique_ptr<XrGeneratedDispatchTable> &dispatch_table = loader_instance->DispatchTable();
+ const std::unique_ptr<XrGeneratedDispatchTableCore> &dispatch_table = loader_instance->DispatchTable();
if (nullptr != dispatch_table->SessionInsertDebugUtilsLabelEXT) {
return dispatch_table->SessionInsertDebugUtilsLabelEXT(session, labelInfo);
}
@@ -643,7 +643,7 @@ XRAPI_ATTR XrResult XRAPI_CALL LoaderXrTermCreateDebugUtilsMessengerEXT(XrInstan
"xrCreateDebugUtilsMessengerEXT", "invalid messenger pointer");
return XR_ERROR_VALIDATION_FAILURE;
}
- const XrGeneratedDispatchTable *dispatch_table = RuntimeInterface::GetDispatchTable(instance);
+ const XrGeneratedDispatchTableCore *dispatch_table = RuntimeInterface::GetDispatchTable(instance);
XrResult result = XR_SUCCESS;
// This extension is supported entirely by the loader which means the runtime may or may not support it.
if (nullptr != dispatch_table->CreateDebugUtilsMessengerEXT) {
@@ -664,7 +664,7 @@ XRLOADER_ABI_CATCH_FALLBACK
XRAPI_ATTR XrResult XRAPI_CALL LoaderXrTermDestroyDebugUtilsMessengerEXT(XrDebugUtilsMessengerEXT messenger) XRLOADER_ABI_TRY {
LoaderLogger::LogVerboseMessage("xrDestroyDebugUtilsMessengerEXT", "Entering loader terminator");
- const XrGeneratedDispatchTable *dispatch_table = RuntimeInterface::GetDebugUtilsMessengerDispatchTable(messenger);
+ const XrGeneratedDispatchTableCore *dispatch_table = RuntimeInterface::GetDebugUtilsMessengerDispatchTable(messenger);
XrResult result = XR_SUCCESS;
LoaderLogger::GetInstance().RemoveLogRecorder(MakeHandleGeneric(messenger));
RuntimeInterface::GetRuntime().ForgetDebugMessenger(messenger);
@@ -684,7 +684,7 @@ XRAPI_ATTR XrResult XRAPI_CALL LoaderXrTermSubmitDebugUtilsMessageEXT(
XrInstance instance, XrDebugUtilsMessageSeverityFlagsEXT messageSeverity, XrDebugUtilsMessageTypeFlagsEXT messageTypes,
const XrDebugUtilsMessengerCallbackDataEXT *callbackData) XRLOADER_ABI_TRY {
LoaderLogger::LogVerboseMessage("xrSubmitDebugUtilsMessageEXT", "Entering loader terminator");
- const XrGeneratedDispatchTable *dispatch_table = RuntimeInterface::GetDispatchTable(instance);
+ const XrGeneratedDispatchTableCore *dispatch_table = RuntimeInterface::GetDispatchTable(instance);
XrResult result = XR_SUCCESS;
if (nullptr != dispatch_table->SubmitDebugUtilsMessageEXT) {
result = dispatch_table->SubmitDebugUtilsMessageEXT(instance, messageSeverity, messageTypes, callbackData);
@@ -701,7 +701,7 @@ XRLOADER_ABI_CATCH_FALLBACK
XRAPI_ATTR XrResult XRAPI_CALL
LoaderXrTermSetDebugUtilsObjectNameEXT(XrInstance instance, const XrDebugUtilsObjectNameInfoEXT *nameInfo) XRLOADER_ABI_TRY {
LoaderLogger::LogVerboseMessage("xrSetDebugUtilsObjectNameEXT", "Entering loader terminator");
- const XrGeneratedDispatchTable *dispatch_table = RuntimeInterface::GetDispatchTable(instance);
+ const XrGeneratedDispatchTableCore *dispatch_table = RuntimeInterface::GetDispatchTable(instance);
XrResult result = XR_SUCCESS;
if (nullptr != dispatch_table->SetDebugUtilsObjectNameEXT) {
result = dispatch_table->SetDebugUtilsObjectNameEXT(instance, nameInfo);
diff --git a/thirdparty/openxr/src/loader/loader_instance.cpp b/thirdparty/openxr/src/loader/loader_instance.cpp
index f18230087f..09521ecf47 100644
--- a/thirdparty/openxr/src/loader/loader_instance.cpp
+++ b/thirdparty/openxr/src/loader/loader_instance.cpp
@@ -278,12 +278,12 @@ LoaderInstance::LoaderInstance(XrInstance instance, const XrInstanceCreateInfo*
: _runtime_instance(instance),
_topmost_gipa(topmost_gipa),
_api_layer_interfaces(std::move(api_layer_interfaces)),
- _dispatch_table(new XrGeneratedDispatchTable{}) {
+ _dispatch_table(new XrGeneratedDispatchTableCore{}) {
for (uint32_t ext = 0; ext < create_info->enabledExtensionCount; ++ext) {
_enabled_extensions.push_back(create_info->enabledExtensionNames[ext]);
}
- GeneratedXrPopulateDispatchTable(_dispatch_table.get(), instance, topmost_gipa);
+ GeneratedXrPopulateDispatchTableCore(_dispatch_table.get(), instance, topmost_gipa);
}
LoaderInstance::~LoaderInstance() {
diff --git a/thirdparty/openxr/src/loader/loader_instance.hpp b/thirdparty/openxr/src/loader/loader_instance.hpp
index b99e6b047f..93cd5dc6a4 100644
--- a/thirdparty/openxr/src/loader/loader_instance.hpp
+++ b/thirdparty/openxr/src/loader/loader_instance.hpp
@@ -23,7 +23,7 @@
#include <vector>
class ApiLayerInterface;
-struct XrGeneratedDispatchTable;
+struct XrGeneratedDispatchTableCore;
class LoaderInstance;
// Manage the single loader instance that is available.
@@ -54,7 +54,7 @@ class LoaderInstance {
virtual ~LoaderInstance();
XrInstance GetInstanceHandle() { return _runtime_instance; }
- const std::unique_ptr<XrGeneratedDispatchTable>& DispatchTable() { return _dispatch_table; }
+ const std::unique_ptr<XrGeneratedDispatchTableCore>& DispatchTable() { return _dispatch_table; }
std::vector<std::unique_ptr<ApiLayerInterface>>& LayerInterfaces() { return _api_layer_interfaces; }
bool ExtensionIsEnabled(const std::string& extension);
XrDebugUtilsMessengerEXT DefaultDebugUtilsMessenger() { return _messenger; }
@@ -71,7 +71,7 @@ class LoaderInstance {
std::vector<std::string> _enabled_extensions;
std::vector<std::unique_ptr<ApiLayerInterface>> _api_layer_interfaces;
- std::unique_ptr<XrGeneratedDispatchTable> _dispatch_table;
+ std::unique_ptr<XrGeneratedDispatchTableCore> _dispatch_table;
// Internal debug messenger created during xrCreateInstance
XrDebugUtilsMessengerEXT _messenger{XR_NULL_HANDLE};
};
diff --git a/thirdparty/openxr/src/loader/manifest_file.cpp b/thirdparty/openxr/src/loader/manifest_file.cpp
index 4e3e5b4947..ca212d3435 100644
--- a/thirdparty/openxr/src/loader/manifest_file.cpp
+++ b/thirdparty/openxr/src/loader/manifest_file.cpp
@@ -75,11 +75,12 @@ static inline bool StringEndsWith(const std::string &value, const std::string &e
}
// If the file found is a manifest file name, add it to the out_files manifest list.
-static void AddIfJson(const std::string &full_file, std::vector<std::string> &manifest_files) {
+static bool AddIfJson(const std::string &full_file, std::vector<std::string> &manifest_files) {
if (full_file.empty() || !StringEndsWith(full_file, ".json")) {
- return;
+ return false;
}
manifest_files.push_back(full_file);
+ return true;
}
// Check the current path for any manifest files. If the provided search_path is a directory, look for
@@ -381,7 +382,6 @@ static void ReadRuntimeDataFilesInRegistry(const std::string &runtime_registry_l
if (ERROR_SUCCESS != open_value) {
LoaderLogger::LogWarningMessage("",
"ReadRuntimeDataFilesInRegistry - failed to open registry key " + full_registry_location);
-
return;
}
@@ -391,7 +391,23 @@ static void ReadRuntimeDataFilesInRegistry(const std::string &runtime_registry_l
LoaderLogger::LogWarningMessage(
"", "ReadRuntimeDataFilesInRegistry - failed to read registry value " + default_runtime_value_name);
} else {
- AddFilesInPath(wide_to_utf8(value_w), false, manifest_files);
+ // Not using AddFilesInPath here (as only api_layer manifest paths allow multiple
+ // separated paths)
+ // Small time-of-check vs time-of-use issue here but it mainly only affects the error message.
+ // It does not introduce a security defect.
+ std::string activeRuntimePath = wide_to_utf8(value_w);
+ if (FileSysUtilsIsRegularFile(activeRuntimePath)) {
+ // If the file exists, try to add it
+ std::string absolute_path;
+ FileSysUtilsGetAbsolutePath(activeRuntimePath, absolute_path);
+ if (!AddIfJson(absolute_path, manifest_files)) {
+ LoaderLogger::LogErrorMessage(
+ "", "ReadRuntimeDataFilesInRegistry - registry runtime path is not json " + activeRuntimePath);
+ }
+ } else {
+ LoaderLogger::LogErrorMessage(
+ "", "ReadRuntimeDataFilesInRegistry - registry runtime path does not exist " + activeRuntimePath);
+ }
}
RegCloseKey(hkey);
@@ -757,8 +773,7 @@ void ApiLayerManifestFile::AddManifestFilesAndroid(const std::string &openxr_com
}
std::istringstream json_stream(std::string{buf, length});
- CreateIfValid(ManifestFileType::MANIFEST_TYPE_EXPLICIT_API_LAYER, filename, json_stream,
- &ApiLayerManifestFile::LocateLibraryInAssets, manifest_files);
+ CreateIfValid(type, filename, json_stream, &ApiLayerManifestFile::LocateLibraryInAssets, manifest_files);
}
}
#endif // defined(XR_USE_PLATFORM_ANDROID) && defined(XR_KHR_LOADER_INIT_SUPPORT)
diff --git a/thirdparty/openxr/src/loader/runtime_interface.cpp b/thirdparty/openxr/src/loader/runtime_interface.cpp
index a0296c738c..32feea6dde 100644
--- a/thirdparty/openxr/src/loader/runtime_interface.cpp
+++ b/thirdparty/openxr/src/loader/runtime_interface.cpp
@@ -126,6 +126,10 @@ XrResult RuntimeInterface::TryLoadingSingleRuntime(const std::string& openxr_com
XrResult res = XR_ERROR_RUNTIME_FAILURE;
if (nullptr != negotiate) {
res = negotiate(&loader_info, &runtime_info);
+ } else {
+ std::string error_message = "RuntimeInterface::LoadRuntime failed to find negotiate function ";
+ error_message += function_name;
+ LoaderLogger::LogErrorMessage(openxr_command, error_message);
}
// If we supposedly succeeded, but got a nullptr for GetInstanceProcAddr
// then something still went wrong, so return with an error.
@@ -270,8 +274,8 @@ XrResult RuntimeInterface::GetInstanceProcAddr(XrInstance instance, const char*
return GetInstance()->_get_instance_proc_addr(instance, name, function);
}
-const XrGeneratedDispatchTable* RuntimeInterface::GetDispatchTable(XrInstance instance) {
- XrGeneratedDispatchTable* table = nullptr;
+const XrGeneratedDispatchTableCore* RuntimeInterface::GetDispatchTable(XrInstance instance) {
+ XrGeneratedDispatchTableCore* table = nullptr;
std::lock_guard<std::mutex> mlock(GetInstance()->_dispatch_table_mutex);
auto it = GetInstance()->_dispatch_table_map.find(instance);
if (it != GetInstance()->_dispatch_table_map.end()) {
@@ -280,7 +284,7 @@ const XrGeneratedDispatchTable* RuntimeInterface::GetDispatchTable(XrInstance in
return table;
}
-const XrGeneratedDispatchTable* RuntimeInterface::GetDebugUtilsMessengerDispatchTable(XrDebugUtilsMessengerEXT messenger) {
+const XrGeneratedDispatchTableCore* RuntimeInterface::GetDebugUtilsMessengerDispatchTable(XrDebugUtilsMessengerEXT messenger) {
XrInstance runtime_instance = XR_NULL_HANDLE;
{
std::lock_guard<std::mutex> mlock(GetInstance()->_messenger_to_instance_mutex);
@@ -349,8 +353,8 @@ XrResult RuntimeInterface::CreateInstance(const XrInstanceCreateInfo* info, XrIn
res = rt_xrCreateInstance(info, instance);
if (XR_SUCCEEDED(res)) {
create_succeeded = true;
- std::unique_ptr<XrGeneratedDispatchTable> dispatch_table(new XrGeneratedDispatchTable());
- GeneratedXrPopulateDispatchTable(dispatch_table.get(), *instance, _get_instance_proc_addr);
+ std::unique_ptr<XrGeneratedDispatchTableCore> dispatch_table(new XrGeneratedDispatchTableCore());
+ GeneratedXrPopulateDispatchTableCore(dispatch_table.get(), *instance, _get_instance_proc_addr);
std::lock_guard<std::mutex> mlock(_dispatch_table_mutex);
_dispatch_table_map[*instance] = std::move(dispatch_table);
}
diff --git a/thirdparty/openxr/src/loader/runtime_interface.hpp b/thirdparty/openxr/src/loader/runtime_interface.hpp
index 093f8ba767..3d45b12946 100644
--- a/thirdparty/openxr/src/loader/runtime_interface.hpp
+++ b/thirdparty/openxr/src/loader/runtime_interface.hpp
@@ -24,7 +24,7 @@ class Value;
}
class RuntimeManifestFile;
-struct XrGeneratedDispatchTable;
+struct XrGeneratedDispatchTableCore;
class RuntimeInterface {
public:
@@ -37,8 +37,8 @@ class RuntimeInterface {
static XrResult GetInstanceProcAddr(XrInstance instance, const char* name, PFN_xrVoidFunction* function);
// Get the direct dispatch table to this runtime, without API layers or loader terminators.
- static const XrGeneratedDispatchTable* GetDispatchTable(XrInstance instance);
- static const XrGeneratedDispatchTable* GetDebugUtilsMessengerDispatchTable(XrDebugUtilsMessengerEXT messenger);
+ static const XrGeneratedDispatchTableCore* GetDispatchTable(XrInstance instance);
+ static const XrGeneratedDispatchTableCore* GetDebugUtilsMessengerDispatchTable(XrDebugUtilsMessengerEXT messenger);
void GetInstanceExtensionProperties(std::vector<XrExtensionProperties>& extension_properties);
bool SupportsExtension(const std::string& extension_name);
@@ -66,7 +66,7 @@ class RuntimeInterface {
LoaderPlatformLibraryHandle _runtime_library;
PFN_xrGetInstanceProcAddr _get_instance_proc_addr;
- std::unordered_map<XrInstance, std::unique_ptr<XrGeneratedDispatchTable>> _dispatch_table_map;
+ std::unordered_map<XrInstance, std::unique_ptr<XrGeneratedDispatchTableCore>> _dispatch_table_map;
std::mutex _dispatch_table_mutex;
std::unordered_map<XrDebugUtilsMessengerEXT, XrInstance> _messenger_to_instance_map;
std::mutex _messenger_to_instance_mutex;
diff --git a/thirdparty/openxr/src/loader/xr_generated_loader.cpp b/thirdparty/openxr/src/loader/xr_generated_loader.cpp
index 7bddbdc3ea..d7f1ed18c1 100644
--- a/thirdparty/openxr/src/loader/xr_generated_loader.cpp
+++ b/thirdparty/openxr/src/loader/xr_generated_loader.cpp
@@ -697,4 +697,17 @@ extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrStopHapticFeedback(
}
XRLOADER_ABI_CATCH_FALLBACK
+extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrLocateSpaces(
+ XrSession session,
+ const XrSpacesLocateInfo* locateInfo,
+ XrSpaceLocations* spaceLocations) XRLOADER_ABI_TRY {
+ LoaderInstance* loader_instance;
+ XrResult result = ActiveLoaderInstance::Get(&loader_instance, "xrLocateSpaces");
+ if (XR_SUCCEEDED(result)) {
+ result = loader_instance->DispatchTable()->LocateSpaces(session, locateInfo, spaceLocations);
+ }
+ return result;
+}
+XRLOADER_ABI_CATCH_FALLBACK
+
diff --git a/thirdparty/openxr/src/loader/xr_generated_loader.hpp b/thirdparty/openxr/src/loader/xr_generated_loader.hpp
index 68a6b9470d..9a6c915421 100644
--- a/thirdparty/openxr/src/loader/xr_generated_loader.hpp
+++ b/thirdparty/openxr/src/loader/xr_generated_loader.hpp
@@ -245,6 +245,10 @@ extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrApplyHapticFeedback(
extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrStopHapticFeedback(
XrSession session,
const XrHapticActionInfo* hapticActionInfo);
+extern "C" LOADER_EXPORT XRAPI_ATTR XrResult XRAPI_CALL xrLocateSpaces(
+ XrSession session,
+ const XrSpacesLocateInfo* locateInfo,
+ XrSpaceLocations* spaceLocations);
#ifdef __cplusplus
} // extern "C"
#endif
diff --git a/thirdparty/openxr/src/xr_generated_dispatch_table_core.c b/thirdparty/openxr/src/xr_generated_dispatch_table_core.c
index e73e8b2b95..aaf854704b 100644
--- a/thirdparty/openxr/src/xr_generated_dispatch_table_core.c
+++ b/thirdparty/openxr/src/xr_generated_dispatch_table_core.c
@@ -37,7 +37,7 @@
extern "C" {
#endif
// Helper function to populate an instance dispatch table
-void GeneratedXrPopulateDispatchTable(struct XrGeneratedDispatchTable *table,
+void GeneratedXrPopulateDispatchTableCore(struct XrGeneratedDispatchTableCore *table,
XrInstance instance,
PFN_xrGetInstanceProcAddr get_inst_proc_addr) {
@@ -96,6 +96,9 @@ void GeneratedXrPopulateDispatchTable(struct XrGeneratedDispatchTable *table,
(get_inst_proc_addr(instance, "xrApplyHapticFeedback", (PFN_xrVoidFunction*)&table->ApplyHapticFeedback));
(get_inst_proc_addr(instance, "xrStopHapticFeedback", (PFN_xrVoidFunction*)&table->StopHapticFeedback));
+ // ---- Core 1.1 commands
+ (get_inst_proc_addr(instance, "xrLocateSpaces", (PFN_xrVoidFunction*)&table->LocateSpaces));
+
// ---- XR_EXT_debug_utils extension commands
(get_inst_proc_addr(instance, "xrSetDebugUtilsObjectNameEXT", (PFN_xrVoidFunction*)&table->SetDebugUtilsObjectNameEXT));
(get_inst_proc_addr(instance, "xrCreateDebugUtilsMessengerEXT", (PFN_xrVoidFunction*)&table->CreateDebugUtilsMessengerEXT));
diff --git a/thirdparty/openxr/src/xr_generated_dispatch_table_core.h b/thirdparty/openxr/src/xr_generated_dispatch_table_core.h
index 5871231267..69abf10b93 100644
--- a/thirdparty/openxr/src/xr_generated_dispatch_table_core.h
+++ b/thirdparty/openxr/src/xr_generated_dispatch_table_core.h
@@ -38,7 +38,7 @@
extern "C" {
#endif
// Generated dispatch table
-struct XrGeneratedDispatchTable {
+struct XrGeneratedDispatchTableCore {
// ---- Core 1.0 commands
PFN_xrGetInstanceProcAddr GetInstanceProcAddr;
@@ -97,6 +97,9 @@ struct XrGeneratedDispatchTable {
PFN_xrApplyHapticFeedback ApplyHapticFeedback;
PFN_xrStopHapticFeedback StopHapticFeedback;
+ // ---- Core 1.1 commands
+ PFN_xrLocateSpaces LocateSpaces;
+
// ---- XR_EXT_debug_utils extension commands
PFN_xrSetDebugUtilsObjectNameEXT SetDebugUtilsObjectNameEXT;
PFN_xrCreateDebugUtilsMessengerEXT CreateDebugUtilsMessengerEXT;
@@ -109,7 +112,7 @@ struct XrGeneratedDispatchTable {
// Prototype for dispatch table helper function
-void GeneratedXrPopulateDispatchTable(struct XrGeneratedDispatchTable *table,
+void GeneratedXrPopulateDispatchTableCore(struct XrGeneratedDispatchTableCore *table,
XrInstance instance,
PFN_xrGetInstanceProcAddr get_inst_proc_addr);
diff --git a/thirdparty/squish/LICENSE.txt b/thirdparty/squish/LICENSE.txt
deleted file mode 100644
index e491e36226..0000000000
--- a/thirdparty/squish/LICENSE.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
-
-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.
diff --git a/thirdparty/squish/alpha.cpp b/thirdparty/squish/alpha.cpp
deleted file mode 100644
index 7039c1a3b8..0000000000
--- a/thirdparty/squish/alpha.cpp
+++ /dev/null
@@ -1,350 +0,0 @@
-/* -----------------------------------------------------------------------------
-
- Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
-
- 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 "alpha.h"
-
-#include <climits>
-#include <algorithm>
-
-namespace squish {
-
-static int FloatToInt( float a, int limit )
-{
- // use ANSI round-to-zero behaviour to get round-to-nearest
- int i = ( int )( a + 0.5f );
-
- // clamp to the limit
- if( i < 0 )
- i = 0;
- else if( i > limit )
- i = limit;
-
- // done
- return i;
-}
-
-void CompressAlphaDxt3( u8 const* rgba, int mask, void* block )
-{
- u8* bytes = reinterpret_cast< u8* >( block );
-
- // quantise and pack the alpha values pairwise
- for( int i = 0; i < 8; ++i )
- {
- // quantise down to 4 bits
- float alpha1 = ( float )rgba[8*i + 3] * ( 15.0f/255.0f );
- float alpha2 = ( float )rgba[8*i + 7] * ( 15.0f/255.0f );
- int quant1 = FloatToInt( alpha1, 15 );
- int quant2 = FloatToInt( alpha2, 15 );
-
- // set alpha to zero where masked
- int bit1 = 1 << ( 2*i );
- int bit2 = 1 << ( 2*i + 1 );
- if( ( mask & bit1 ) == 0 )
- quant1 = 0;
- if( ( mask & bit2 ) == 0 )
- quant2 = 0;
-
- // pack into the byte
- bytes[i] = ( u8 )( quant1 | ( quant2 << 4 ) );
- }
-}
-
-void DecompressAlphaDxt3( u8* rgba, void const* block )
-{
- u8 const* bytes = reinterpret_cast< u8 const* >( block );
-
- // unpack the alpha values pairwise
- for( int i = 0; i < 8; ++i )
- {
- // quantise down to 4 bits
- u8 quant = bytes[i];
-
- // unpack the values
- u8 lo = quant & 0x0f;
- u8 hi = quant & 0xf0;
-
- // convert back up to bytes
- rgba[8*i + 3] = lo | ( lo << 4 );
- rgba[8*i + 7] = hi | ( hi >> 4 );
- }
-}
-
-static void FixRange( int& min, int& max, int steps )
-{
- if( max - min < steps )
- max = std::min( min + steps, 255 );
- if( max - min < steps )
- min = std::max( 0, max - steps );
-}
-
-static int FitCodes( u8 const* rgba, int mask, u8 const* codes, u8* indices )
-{
- // fit each alpha value to the codebook
- int err = 0;
- for( int i = 0; i < 16; ++i )
- {
- // check this pixel is valid
- int bit = 1 << i;
- if( ( mask & bit ) == 0 )
- {
- // use the first code
- indices[i] = 0;
- continue;
- }
-
- // find the least error and corresponding index
- int value = rgba[4*i + 3];
- int least = INT_MAX;
- int index = 0;
- for( int j = 0; j < 8; ++j )
- {
- // get the squared error from this code
- int dist = ( int )value - ( int )codes[j];
- dist *= dist;
-
- // compare with the best so far
- if( dist < least )
- {
- least = dist;
- index = j;
- }
- }
-
- // save this index and accumulate the error
- indices[i] = ( u8 )index;
- err += least;
- }
-
- // return the total error
- return err;
-}
-
-static void WriteAlphaBlock( int alpha0, int alpha1, u8 const* indices, void* block )
-{
- u8* bytes = reinterpret_cast< u8* >( block );
-
- // write the first two bytes
- bytes[0] = ( u8 )alpha0;
- bytes[1] = ( u8 )alpha1;
-
- // pack the indices with 3 bits each
- u8* dest = bytes + 2;
- u8 const* src = indices;
- for( int i = 0; i < 2; ++i )
- {
- // pack 8 3-bit values
- int value = 0;
- for( int j = 0; j < 8; ++j )
- {
- int index = *src++;
- value |= ( index << 3*j );
- }
-
- // store in 3 bytes
- for( int j = 0; j < 3; ++j )
- {
- int byte = ( value >> 8*j ) & 0xff;
- *dest++ = ( u8 )byte;
- }
- }
-}
-
-static void WriteAlphaBlock5( int alpha0, int alpha1, u8 const* indices, void* block )
-{
- // check the relative values of the endpoints
- if( alpha0 > alpha1 )
- {
- // swap the indices
- u8 swapped[16];
- for( int i = 0; i < 16; ++i )
- {
- u8 index = indices[i];
- if( index == 0 )
- swapped[i] = 1;
- else if( index == 1 )
- swapped[i] = 0;
- else if( index <= 5 )
- swapped[i] = 7 - index;
- else
- swapped[i] = index;
- }
-
- // write the block
- WriteAlphaBlock( alpha1, alpha0, swapped, block );
- }
- else
- {
- // write the block
- WriteAlphaBlock( alpha0, alpha1, indices, block );
- }
-}
-
-static void WriteAlphaBlock7( int alpha0, int alpha1, u8 const* indices, void* block )
-{
- // check the relative values of the endpoints
- if( alpha0 < alpha1 )
- {
- // swap the indices
- u8 swapped[16];
- for( int i = 0; i < 16; ++i )
- {
- u8 index = indices[i];
- if( index == 0 )
- swapped[i] = 1;
- else if( index == 1 )
- swapped[i] = 0;
- else
- swapped[i] = 9 - index;
- }
-
- // write the block
- WriteAlphaBlock( alpha1, alpha0, swapped, block );
- }
- else
- {
- // write the block
- WriteAlphaBlock( alpha0, alpha1, indices, block );
- }
-}
-
-void CompressAlphaDxt5( u8 const* rgba, int mask, void* block )
-{
- // get the range for 5-alpha and 7-alpha interpolation
- int min5 = 255;
- int max5 = 0;
- int min7 = 255;
- int max7 = 0;
- for( int i = 0; i < 16; ++i )
- {
- // check this pixel is valid
- int bit = 1 << i;
- if( ( mask & bit ) == 0 )
- continue;
-
- // incorporate into the min/max
- int value = rgba[4*i + 3];
- if( value < min7 )
- min7 = value;
- if( value > max7 )
- max7 = value;
- if( value != 0 && value < min5 )
- min5 = value;
- if( value != 255 && value > max5 )
- max5 = value;
- }
-
- // handle the case that no valid range was found
- if( min5 > max5 )
- min5 = max5;
- if( min7 > max7 )
- min7 = max7;
-
- // fix the range to be the minimum in each case
- FixRange( min5, max5, 5 );
- FixRange( min7, max7, 7 );
-
- // set up the 5-alpha code book
- u8 codes5[8];
- codes5[0] = ( u8 )min5;
- codes5[1] = ( u8 )max5;
- for( int i = 1; i < 5; ++i )
- codes5[1 + i] = ( u8 )( ( ( 5 - i )*min5 + i*max5 )/5 );
- codes5[6] = 0;
- codes5[7] = 255;
-
- // set up the 7-alpha code book
- u8 codes7[8];
- codes7[0] = ( u8 )min7;
- codes7[1] = ( u8 )max7;
- for( int i = 1; i < 7; ++i )
- codes7[1 + i] = ( u8 )( ( ( 7 - i )*min7 + i*max7 )/7 );
-
- // fit the data to both code books
- u8 indices5[16];
- u8 indices7[16];
- int err5 = FitCodes( rgba, mask, codes5, indices5 );
- int err7 = FitCodes( rgba, mask, codes7, indices7 );
-
- // save the block with least error
- if( err5 <= err7 )
- WriteAlphaBlock5( min5, max5, indices5, block );
- else
- WriteAlphaBlock7( min7, max7, indices7, block );
-}
-
-void DecompressAlphaDxt5( u8* rgba, void const* block )
-{
- // get the two alpha values
- u8 const* bytes = reinterpret_cast< u8 const* >( block );
- int alpha0 = bytes[0];
- int alpha1 = bytes[1];
-
- // compare the values to build the codebook
- u8 codes[8];
- codes[0] = ( u8 )alpha0;
- codes[1] = ( u8 )alpha1;
- if( alpha0 <= alpha1 )
- {
- // use 5-alpha codebook
- for( int i = 1; i < 5; ++i )
- codes[1 + i] = ( u8 )( ( ( 5 - i )*alpha0 + i*alpha1 )/5 );
- codes[6] = 0;
- codes[7] = 255;
- }
- else
- {
- // use 7-alpha codebook
- for( int i = 1; i < 7; ++i )
- codes[1 + i] = ( u8 )( ( ( 7 - i )*alpha0 + i*alpha1 )/7 );
- }
-
- // decode the indices
- u8 indices[16];
- u8 const* src = bytes + 2;
- u8* dest = indices;
- for( int i = 0; i < 2; ++i )
- {
- // grab 3 bytes
- int value = 0;
- for( int j = 0; j < 3; ++j )
- {
- int byte = *src++;
- value |= ( byte << 8*j );
- }
-
- // unpack 8 3-bit values from it
- for( int j = 0; j < 8; ++j )
- {
- int index = ( value >> 3*j ) & 0x7;
- *dest++ = ( u8 )index;
- }
- }
-
- // write out the indexed codebook values
- for( int i = 0; i < 16; ++i )
- rgba[4*i + 3] = codes[indices[i]];
-}
-
-} // namespace squish
diff --git a/thirdparty/squish/alpha.h b/thirdparty/squish/alpha.h
deleted file mode 100644
index a1fffd4049..0000000000
--- a/thirdparty/squish/alpha.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* -----------------------------------------------------------------------------
-
- Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
-
- 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 SQUISH_ALPHA_H
-#define SQUISH_ALPHA_H
-
-#include "squish.h"
-
-namespace squish {
-
-void CompressAlphaDxt3( u8 const* rgba, int mask, void* block );
-void CompressAlphaDxt5( u8 const* rgba, int mask, void* block );
-
-void DecompressAlphaDxt3( u8* rgba, void const* block );
-void DecompressAlphaDxt5( u8* rgba, void const* block );
-
-} // namespace squish
-
-#endif // ndef SQUISH_ALPHA_H
diff --git a/thirdparty/squish/clusterfit.cpp b/thirdparty/squish/clusterfit.cpp
deleted file mode 100644
index 1610ecb5d8..0000000000
--- a/thirdparty/squish/clusterfit.cpp
+++ /dev/null
@@ -1,392 +0,0 @@
-/* -----------------------------------------------------------------------------
-
- Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
- Copyright (c) 2007 Ignacio Castano icastano@nvidia.com
-
- 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 "clusterfit.h"
-#include "colourset.h"
-#include "colourblock.h"
-#include <cfloat>
-
-namespace squish {
-
-ClusterFit::ClusterFit( ColourSet const* colours, int flags, float* metric )
- : ColourFit( colours, flags )
-{
- // set the iteration count
- m_iterationCount = ( m_flags & kColourIterativeClusterFit ) ? kMaxIterations : 1;
-
- // initialise the metric (old perceptual = 0.2126f, 0.7152f, 0.0722f)
- if( metric )
- m_metric = Vec4( metric[0], metric[1], metric[2], 1.0f );
- else
- m_metric = VEC4_CONST( 1.0f );
-
- // initialise the best error
- m_besterror = VEC4_CONST( FLT_MAX );
-
- // cache some values
- int const count = m_colours->GetCount();
- Vec3 const* values = m_colours->GetPoints();
-
- // get the covariance matrix
- Sym3x3 covariance = ComputeWeightedCovariance( count, values, m_colours->GetWeights() );
-
- // compute the principle component
- m_principle = ComputePrincipleComponent( covariance );
-}
-
-bool ClusterFit::ConstructOrdering( Vec3 const& axis, int iteration )
-{
- // cache some values
- int const count = m_colours->GetCount();
- Vec3 const* values = m_colours->GetPoints();
-
- // build the list of dot products
- float dps[16];
- u8* order = ( u8* )m_order + 16*iteration;
- for( int i = 0; i < count; ++i )
- {
- dps[i] = Dot( values[i], axis );
- order[i] = ( u8 )i;
- }
-
- // stable sort using them
- for( int i = 0; i < count; ++i )
- {
- for( int j = i; j > 0 && dps[j] < dps[j - 1]; --j )
- {
- std::swap( dps[j], dps[j - 1] );
- std::swap( order[j], order[j - 1] );
- }
- }
-
- // check this ordering is unique
- for( int it = 0; it < iteration; ++it )
- {
- u8 const* prev = ( u8* )m_order + 16*it;
- bool same = true;
- for( int i = 0; i < count; ++i )
- {
- if( order[i] != prev[i] )
- {
- same = false;
- break;
- }
- }
- if( same )
- return false;
- }
-
- // copy the ordering and weight all the points
- Vec3 const* unweighted = m_colours->GetPoints();
- float const* weights = m_colours->GetWeights();
- m_xsum_wsum = VEC4_CONST( 0.0f );
- for( int i = 0; i < count; ++i )
- {
- int j = order[i];
- Vec4 p( unweighted[j].X(), unweighted[j].Y(), unweighted[j].Z(), 1.0f );
- Vec4 w( weights[j] );
- Vec4 x = p*w;
- m_points_weights[i] = x;
- m_xsum_wsum += x;
- }
- return true;
-}
-
-void ClusterFit::Compress3( void* block )
-{
- // declare variables
- int const count = m_colours->GetCount();
- Vec4 const two = VEC4_CONST( 2.0 );
- Vec4 const one = VEC4_CONST( 1.0f );
- Vec4 const half_half2( 0.5f, 0.5f, 0.5f, 0.25f );
- Vec4 const zero = VEC4_CONST( 0.0f );
- Vec4 const half = VEC4_CONST( 0.5f );
- Vec4 const grid( 31.0f, 63.0f, 31.0f, 0.0f );
- Vec4 const gridrcp( 1.0f/31.0f, 1.0f/63.0f, 1.0f/31.0f, 0.0f );
-
- // prepare an ordering using the principle axis
- ConstructOrdering( m_principle, 0 );
-
- // check all possible clusters and iterate on the total order
- Vec4 beststart = VEC4_CONST( 0.0f );
- Vec4 bestend = VEC4_CONST( 0.0f );
- Vec4 besterror = m_besterror;
- u8 bestindices[16];
- int bestiteration = 0;
- int besti = 0, bestj = 0;
-
- // loop over iterations (we avoid the case that all points in first or last cluster)
- for( int iterationIndex = 0;; )
- {
- // first cluster [0,i) is at the start
- Vec4 part0 = VEC4_CONST( 0.0f );
- for( int i = 0; i < count; ++i )
- {
- // second cluster [i,j) is half along
- Vec4 part1 = ( i == 0 ) ? m_points_weights[0] : VEC4_CONST( 0.0f );
- int jmin = ( i == 0 ) ? 1 : i;
- for( int j = jmin;; )
- {
- // last cluster [j,count) is at the end
- Vec4 part2 = m_xsum_wsum - part1 - part0;
-
- // compute least squares terms directly
- Vec4 alphax_sum = MultiplyAdd( part1, half_half2, part0 );
- Vec4 alpha2_sum = alphax_sum.SplatW();
-
- Vec4 betax_sum = MultiplyAdd( part1, half_half2, part2 );
- Vec4 beta2_sum = betax_sum.SplatW();
-
- Vec4 alphabeta_sum = ( part1*half_half2 ).SplatW();
-
- // compute the least-squares optimal points
- Vec4 factor = Reciprocal( NegativeMultiplySubtract( alphabeta_sum, alphabeta_sum, alpha2_sum*beta2_sum ) );
- Vec4 a = NegativeMultiplySubtract( betax_sum, alphabeta_sum, alphax_sum*beta2_sum )*factor;
- Vec4 b = NegativeMultiplySubtract( alphax_sum, alphabeta_sum, betax_sum*alpha2_sum )*factor;
-
- // clamp to the grid
- a = Min( one, Max( zero, a ) );
- b = Min( one, Max( zero, b ) );
- a = Truncate( MultiplyAdd( grid, a, half ) )*gridrcp;
- b = Truncate( MultiplyAdd( grid, b, half ) )*gridrcp;
-
- // compute the error (we skip the constant xxsum)
- Vec4 e1 = MultiplyAdd( a*a, alpha2_sum, b*b*beta2_sum );
- Vec4 e2 = NegativeMultiplySubtract( a, alphax_sum, a*b*alphabeta_sum );
- Vec4 e3 = NegativeMultiplySubtract( b, betax_sum, e2 );
- Vec4 e4 = MultiplyAdd( two, e3, e1 );
-
- // apply the metric to the error term
- Vec4 e5 = e4*m_metric;
- Vec4 error = e5.SplatX() + e5.SplatY() + e5.SplatZ();
-
- // keep the solution if it wins
- if( CompareAnyLessThan( error, besterror ) )
- {
- beststart = a;
- bestend = b;
- besti = i;
- bestj = j;
- besterror = error;
- bestiteration = iterationIndex;
- }
-
- // advance
- if( j == count )
- break;
- part1 += m_points_weights[j];
- ++j;
- }
-
- // advance
- part0 += m_points_weights[i];
- }
-
- // stop if we didn't improve in this iteration
- if( bestiteration != iterationIndex )
- break;
-
- // advance if possible
- ++iterationIndex;
- if( iterationIndex == m_iterationCount )
- break;
-
- // stop if a new iteration is an ordering that has already been tried
- Vec3 axis = ( bestend - beststart ).GetVec3();
- if( !ConstructOrdering( axis, iterationIndex ) )
- break;
- }
-
- // save the block if necessary
- if( CompareAnyLessThan( besterror, m_besterror ) )
- {
- // remap the indices
- u8 const* order = ( u8* )m_order + 16*bestiteration;
-
- u8 unordered[16];
- for( int m = 0; m < besti; ++m )
- unordered[order[m]] = 0;
- for( int m = besti; m < bestj; ++m )
- unordered[order[m]] = 2;
- for( int m = bestj; m < count; ++m )
- unordered[order[m]] = 1;
-
- m_colours->RemapIndices( unordered, bestindices );
-
- // save the block
- WriteColourBlock3( beststart.GetVec3(), bestend.GetVec3(), bestindices, block );
-
- // save the error
- m_besterror = besterror;
- }
-}
-
-void ClusterFit::Compress4( void* block )
-{
- // declare variables
- int const count = m_colours->GetCount();
- Vec4 const two = VEC4_CONST( 2.0f );
- Vec4 const one = VEC4_CONST( 1.0f );
- Vec4 const onethird_onethird2( 1.0f/3.0f, 1.0f/3.0f, 1.0f/3.0f, 1.0f/9.0f );
- Vec4 const twothirds_twothirds2( 2.0f/3.0f, 2.0f/3.0f, 2.0f/3.0f, 4.0f/9.0f );
- Vec4 const twonineths = VEC4_CONST( 2.0f/9.0f );
- Vec4 const zero = VEC4_CONST( 0.0f );
- Vec4 const half = VEC4_CONST( 0.5f );
- Vec4 const grid( 31.0f, 63.0f, 31.0f, 0.0f );
- Vec4 const gridrcp( 1.0f/31.0f, 1.0f/63.0f, 1.0f/31.0f, 0.0f );
-
- // prepare an ordering using the principle axis
- ConstructOrdering( m_principle, 0 );
-
- // check all possible clusters and iterate on the total order
- Vec4 beststart = VEC4_CONST( 0.0f );
- Vec4 bestend = VEC4_CONST( 0.0f );
- Vec4 besterror = m_besterror;
- u8 bestindices[16];
- int bestiteration = 0;
- int besti = 0, bestj = 0, bestk = 0;
-
- // loop over iterations (we avoid the case that all points in first or last cluster)
- for( int iterationIndex = 0;; )
- {
- // first cluster [0,i) is at the start
- Vec4 part0 = VEC4_CONST( 0.0f );
- for( int i = 0; i < count; ++i )
- {
- // second cluster [i,j) is one third along
- Vec4 part1 = VEC4_CONST( 0.0f );
- for( int j = i;; )
- {
- // third cluster [j,k) is two thirds along
- Vec4 part2 = ( j == 0 ) ? m_points_weights[0] : VEC4_CONST( 0.0f );
- int kmin = ( j == 0 ) ? 1 : j;
- for( int k = kmin;; )
- {
- // last cluster [k,count) is at the end
- Vec4 part3 = m_xsum_wsum - part2 - part1 - part0;
-
- // compute least squares terms directly
- Vec4 const alphax_sum = MultiplyAdd( part2, onethird_onethird2, MultiplyAdd( part1, twothirds_twothirds2, part0 ) );
- Vec4 const alpha2_sum = alphax_sum.SplatW();
-
- Vec4 const betax_sum = MultiplyAdd( part1, onethird_onethird2, MultiplyAdd( part2, twothirds_twothirds2, part3 ) );
- Vec4 const beta2_sum = betax_sum.SplatW();
-
- Vec4 const alphabeta_sum = twonineths*( part1 + part2 ).SplatW();
-
- // compute the least-squares optimal points
- Vec4 factor = Reciprocal( NegativeMultiplySubtract( alphabeta_sum, alphabeta_sum, alpha2_sum*beta2_sum ) );
- Vec4 a = NegativeMultiplySubtract( betax_sum, alphabeta_sum, alphax_sum*beta2_sum )*factor;
- Vec4 b = NegativeMultiplySubtract( alphax_sum, alphabeta_sum, betax_sum*alpha2_sum )*factor;
-
- // clamp to the grid
- a = Min( one, Max( zero, a ) );
- b = Min( one, Max( zero, b ) );
- a = Truncate( MultiplyAdd( grid, a, half ) )*gridrcp;
- b = Truncate( MultiplyAdd( grid, b, half ) )*gridrcp;
-
- // compute the error (we skip the constant xxsum)
- Vec4 e1 = MultiplyAdd( a*a, alpha2_sum, b*b*beta2_sum );
- Vec4 e2 = NegativeMultiplySubtract( a, alphax_sum, a*b*alphabeta_sum );
- Vec4 e3 = NegativeMultiplySubtract( b, betax_sum, e2 );
- Vec4 e4 = MultiplyAdd( two, e3, e1 );
-
- // apply the metric to the error term
- Vec4 e5 = e4*m_metric;
- Vec4 error = e5.SplatX() + e5.SplatY() + e5.SplatZ();
-
- // keep the solution if it wins
- if( CompareAnyLessThan( error, besterror ) )
- {
- beststart = a;
- bestend = b;
- besterror = error;
- besti = i;
- bestj = j;
- bestk = k;
- bestiteration = iterationIndex;
- }
-
- // advance
- if( k == count )
- break;
- part2 += m_points_weights[k];
- ++k;
- }
-
- // advance
- if( j == count )
- break;
- part1 += m_points_weights[j];
- ++j;
- }
-
- // advance
- part0 += m_points_weights[i];
- }
-
- // stop if we didn't improve in this iteration
- if( bestiteration != iterationIndex )
- break;
-
- // advance if possible
- ++iterationIndex;
- if( iterationIndex == m_iterationCount )
- break;
-
- // stop if a new iteration is an ordering that has already been tried
- Vec3 axis = ( bestend - beststart ).GetVec3();
- if( !ConstructOrdering( axis, iterationIndex ) )
- break;
- }
-
- // save the block if necessary
- if( CompareAnyLessThan( besterror, m_besterror ) )
- {
- // remap the indices
- u8 const* order = ( u8* )m_order + 16*bestiteration;
-
- u8 unordered[16];
- for( int m = 0; m < besti; ++m )
- unordered[order[m]] = 0;
- for( int m = besti; m < bestj; ++m )
- unordered[order[m]] = 2;
- for( int m = bestj; m < bestk; ++m )
- unordered[order[m]] = 3;
- for( int m = bestk; m < count; ++m )
- unordered[order[m]] = 1;
-
- m_colours->RemapIndices( unordered, bestindices );
-
- // save the block
- WriteColourBlock4( beststart.GetVec3(), bestend.GetVec3(), bestindices, block );
-
- // save the error
- m_besterror = besterror;
- }
-}
-
-} // namespace squish
diff --git a/thirdparty/squish/clusterfit.h b/thirdparty/squish/clusterfit.h
deleted file mode 100644
index 999396b262..0000000000
--- a/thirdparty/squish/clusterfit.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/* -----------------------------------------------------------------------------
-
- Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
- Copyright (c) 2007 Ignacio Castano icastano@nvidia.com
-
- 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 SQUISH_CLUSTERFIT_H
-#define SQUISH_CLUSTERFIT_H
-
-#include "squish.h"
-#include "maths.h"
-#include "simd.h"
-#include "colourfit.h"
-
-namespace squish {
-
-class ClusterFit : public ColourFit
-{
-public:
- ClusterFit( ColourSet const* colours, int flags, float* metric );
-
-private:
- bool ConstructOrdering( Vec3 const& axis, int iteration );
-
- virtual void Compress3( void* block );
- virtual void Compress4( void* block );
-
- enum { kMaxIterations = 8 };
-
- int m_iterationCount;
- Vec3 m_principle;
- u8 m_order[16*kMaxIterations];
- Vec4 m_points_weights[16];
- Vec4 m_xsum_wsum;
- Vec4 m_metric;
- Vec4 m_besterror;
-};
-
-} // namespace squish
-
-#endif // ndef SQUISH_CLUSTERFIT_H
diff --git a/thirdparty/squish/colourblock.cpp b/thirdparty/squish/colourblock.cpp
deleted file mode 100644
index f14c9362bd..0000000000
--- a/thirdparty/squish/colourblock.cpp
+++ /dev/null
@@ -1,247 +0,0 @@
-/* -----------------------------------------------------------------------------
-
- Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
-
- 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 "colourblock.h"
-// -- GODOT start --
-#include "alpha.h"
-// -- GODOT end --
-
-namespace squish {
-
-static int FloatToInt( float a, int limit )
-{
- // use ANSI round-to-zero behaviour to get round-to-nearest
- int i = ( int )( a + 0.5f );
-
- // clamp to the limit
- if( i < 0 )
- i = 0;
- else if( i > limit )
- i = limit;
-
- // done
- return i;
-}
-
-static int FloatTo565( Vec3::Arg colour )
-{
- // get the components in the correct range
- int r = FloatToInt( 31.0f*colour.X(), 31 );
- int g = FloatToInt( 63.0f*colour.Y(), 63 );
- int b = FloatToInt( 31.0f*colour.Z(), 31 );
-
- // pack into a single value
- return ( r << 11 ) | ( g << 5 ) | b;
-}
-
-static void WriteColourBlock( int a, int b, u8* indices, void* block )
-{
- // get the block as bytes
- u8* bytes = ( u8* )block;
-
- // write the endpoints
- bytes[0] = ( u8 )( a & 0xff );
- bytes[1] = ( u8 )( a >> 8 );
- bytes[2] = ( u8 )( b & 0xff );
- bytes[3] = ( u8 )( b >> 8 );
-
- // write the indices
- for( int i = 0; i < 4; ++i )
- {
- u8 const* ind = indices + 4*i;
- bytes[4 + i] = ind[0] | ( ind[1] << 2 ) | ( ind[2] << 4 ) | ( ind[3] << 6 );
- }
-}
-
-void WriteColourBlock3( Vec3::Arg start, Vec3::Arg end, u8 const* indices, void* block )
-{
- // get the packed values
- int a = FloatTo565( start );
- int b = FloatTo565( end );
-
- // remap the indices
- u8 remapped[16];
- if( a <= b )
- {
- // use the indices directly
- for( int i = 0; i < 16; ++i )
- remapped[i] = indices[i];
- }
- else
- {
- // swap a and b
- std::swap( a, b );
- for( int i = 0; i < 16; ++i )
- {
- if( indices[i] == 0 )
- remapped[i] = 1;
- else if( indices[i] == 1 )
- remapped[i] = 0;
- else
- remapped[i] = indices[i];
- }
- }
-
- // write the block
- WriteColourBlock( a, b, remapped, block );
-}
-
-void WriteColourBlock4( Vec3::Arg start, Vec3::Arg end, u8 const* indices, void* block )
-{
- // get the packed values
- int a = FloatTo565( start );
- int b = FloatTo565( end );
-
- // remap the indices
- u8 remapped[16];
- if( a < b )
- {
- // swap a and b
- std::swap( a, b );
- for( int i = 0; i < 16; ++i )
- remapped[i] = ( indices[i] ^ 0x1 ) & 0x3;
- }
- else if( a == b )
- {
- // use index 0
- for( int i = 0; i < 16; ++i )
- remapped[i] = 0;
- }
- else
- {
- // use the indices directly
- for( int i = 0; i < 16; ++i )
- remapped[i] = indices[i];
- }
-
- // write the block
- WriteColourBlock( a, b, remapped, block );
-}
-
-static int Unpack565( u8 const* packed, u8* colour )
-{
- // build the packed value
- int value = ( int )packed[0] | ( ( int )packed[1] << 8 );
-
- // get the components in the stored range
- u8 red = ( u8 )( ( value >> 11 ) & 0x1f );
- u8 green = ( u8 )( ( value >> 5 ) & 0x3f );
- u8 blue = ( u8 )( value & 0x1f );
-
- // scale up to 8 bits
- colour[0] = ( red << 3 ) | ( red >> 2 );
- colour[1] = ( green << 2 ) | ( green >> 4 );
- colour[2] = ( blue << 3 ) | ( blue >> 2 );
- colour[3] = 255;
-
- // return the value
- return value;
-}
-
-void DecompressColour( u8* rgba, void const* block, bool isDxt1 )
-{
- // get the block bytes
- u8 const* bytes = reinterpret_cast< u8 const* >( block );
-
- // unpack the endpoints
- u8 codes[16];
- int a = Unpack565( bytes, codes );
- int b = Unpack565( bytes + 2, codes + 4 );
-
- // generate the midpoints
- for( int i = 0; i < 3; ++i )
- {
- int c = codes[i];
- int d = codes[4 + i];
-
- if( isDxt1 && a <= b )
- {
- codes[8 + i] = ( u8 )( ( c + d )/2 );
- codes[12 + i] = 0;
- }
- else
- {
- codes[8 + i] = ( u8 )( ( 2*c + d )/3 );
- codes[12 + i] = ( u8 )( ( c + 2*d )/3 );
- }
- }
-
- // fill in alpha for the intermediate values
- codes[8 + 3] = 255;
- codes[12 + 3] = ( isDxt1 && a <= b ) ? 0 : 255;
-
- // unpack the indices
- u8 indices[16];
- for( int i = 0; i < 4; ++i )
- {
- u8* ind = indices + 4*i;
- u8 packed = bytes[4 + i];
-
- ind[0] = packed & 0x3;
- ind[1] = ( packed >> 2 ) & 0x3;
- ind[2] = ( packed >> 4 ) & 0x3;
- ind[3] = ( packed >> 6 ) & 0x3;
- }
-
- // store out the colours
- for( int i = 0; i < 16; ++i )
- {
- u8 offset = 4*indices[i];
- for( int j = 0; j < 4; ++j )
- rgba[4*i + j] = codes[offset + j];
- }
-}
-
-// -- GODOT start --
-void DecompressColourBc4( u8* rgba, void const* block)
-{
- DecompressAlphaDxt5(rgba,block);
- for ( int i = 0; i < 16; ++i ) {
- rgba[i*4] = rgba[i*4 + 3];
- rgba[i*4 + 1] = 0;
- rgba[i*4 + 2] = 0;
- rgba[i*4 + 3] = 255;
- }
-}
-
-void DecompressColourBc5( u8* rgba, void const* block)
-{
- void const* rblock = block;
- void const* gblock = reinterpret_cast< u8 const* >( block ) + 8;
- DecompressAlphaDxt5(rgba,rblock);
- for ( int i = 0; i < 16; ++i ) {
- rgba[i*4] = rgba[i*4 + 3];
- }
- DecompressAlphaDxt5(rgba,gblock);
- for ( int i = 0; i < 16; ++i ) {
- rgba[i*4+1] = rgba[i*4 + 3];
- rgba[i*4 + 2] = 0;
- rgba[i*4 + 3] = 255;
- }
-}
-// -- GODOT end --
-
-
-} // namespace squish
diff --git a/thirdparty/squish/colourblock.h b/thirdparty/squish/colourblock.h
deleted file mode 100644
index e1eb9e4917..0000000000
--- a/thirdparty/squish/colourblock.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/* -----------------------------------------------------------------------------
-
- Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
-
- 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 SQUISH_COLOURBLOCK_H
-#define SQUISH_COLOURBLOCK_H
-
-#include "squish.h"
-#include "maths.h"
-
-namespace squish {
-
-void WriteColourBlock3( Vec3::Arg start, Vec3::Arg end, u8 const* indices, void* block );
-void WriteColourBlock4( Vec3::Arg start, Vec3::Arg end, u8 const* indices, void* block );
-
-void DecompressColour( u8* rgba, void const* block, bool isDxt1 );
-// -- GODOT start --
-void DecompressColourBc4( u8* rgba, void const* block );
-void DecompressColourBc5( u8* rgba, void const* block );
-// -- GODOT end --
-
-} // namespace squish
-
-#endif // ndef SQUISH_COLOURBLOCK_H
diff --git a/thirdparty/squish/colourfit.cpp b/thirdparty/squish/colourfit.cpp
deleted file mode 100644
index e45b656557..0000000000
--- a/thirdparty/squish/colourfit.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-/* -----------------------------------------------------------------------------
-
- Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
-
- 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 "colourfit.h"
-#include "colourset.h"
-
-namespace squish {
-
-ColourFit::ColourFit( ColourSet const* colours, int flags )
- : m_colours( colours ),
- m_flags( flags )
-{
-}
-
-ColourFit::~ColourFit()
-{
-}
-
-void ColourFit::Compress( void* block )
-{
- bool isDxt1 = ( ( m_flags & kDxt1 ) != 0 );
- if( isDxt1 )
- {
- Compress3( block );
- if( !m_colours->IsTransparent() )
- Compress4( block );
- }
- else
- Compress4( block );
-}
-
-} // namespace squish
diff --git a/thirdparty/squish/colourfit.h b/thirdparty/squish/colourfit.h
deleted file mode 100644
index e73dceb2eb..0000000000
--- a/thirdparty/squish/colourfit.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/* -----------------------------------------------------------------------------
-
- Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
-
- 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 SQUISH_COLOURFIT_H
-#define SQUISH_COLOURFIT_H
-
-#include "squish.h"
-#include "maths.h"
-
-#include <climits>
-
-namespace squish {
-
-class ColourSet;
-
-class ColourFit
-{
-public:
- ColourFit( ColourSet const* colours, int flags );
- virtual ~ColourFit();
-
- void Compress( void* block );
-
-protected:
- virtual void Compress3( void* block ) = 0;
- virtual void Compress4( void* block ) = 0;
-
- ColourSet const* m_colours;
- int m_flags;
-};
-
-} // namespace squish
-
-#endif // ndef SQUISH_COLOURFIT_H
diff --git a/thirdparty/squish/colourset.cpp b/thirdparty/squish/colourset.cpp
deleted file mode 100644
index e900556471..0000000000
--- a/thirdparty/squish/colourset.cpp
+++ /dev/null
@@ -1,121 +0,0 @@
-/* -----------------------------------------------------------------------------
-
- Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
-
- 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 "colourset.h"
-
-namespace squish {
-
-ColourSet::ColourSet( u8 const* rgba, int mask, int flags )
- : m_count( 0 ),
- m_transparent( false )
-{
- // check the compression mode for dxt1
- bool isDxt1 = ( ( flags & kDxt1 ) != 0 );
- bool weightByAlpha = ( ( flags & kWeightColourByAlpha ) != 0 );
-
- // create the minimal set
- for( int i = 0; i < 16; ++i )
- {
- // check this pixel is enabled
- int bit = 1 << i;
- if( ( mask & bit ) == 0 )
- {
- m_remap[i] = -1;
- continue;
- }
-
- // check for transparent pixels when using dxt1
- if( isDxt1 && rgba[4*i + 3] < 128 )
- {
- m_remap[i] = -1;
- m_transparent = true;
- continue;
- }
-
- // loop over previous points for a match
- for( int j = 0;; ++j )
- {
- // allocate a new point
- if( j == i )
- {
- // normalise coordinates to [0,1]
- float x = ( float )rgba[4*i] / 255.0f;
- float y = ( float )rgba[4*i + 1] / 255.0f;
- float z = ( float )rgba[4*i + 2] / 255.0f;
-
- // ensure there is always non-zero weight even for zero alpha
- float w = ( float )( rgba[4*i + 3] + 1 ) / 256.0f;
-
- // add the point
- m_points[m_count] = Vec3( x, y, z );
- m_weights[m_count] = ( weightByAlpha ? w : 1.0f );
- m_remap[i] = m_count;
-
- // advance
- ++m_count;
- break;
- }
-
- // check for a match
- int oldbit = 1 << j;
- bool match = ( ( mask & oldbit ) != 0 )
- && ( rgba[4*i] == rgba[4*j] )
- && ( rgba[4*i + 1] == rgba[4*j + 1] )
- && ( rgba[4*i + 2] == rgba[4*j + 2] )
- && ( rgba[4*j + 3] >= 128 || !isDxt1 );
- if( match )
- {
- // get the index of the match
- int index = m_remap[j];
-
- // ensure there is always non-zero weight even for zero alpha
- float w = ( float )( rgba[4*i + 3] + 1 ) / 256.0f;
-
- // map to this point and increase the weight
- m_weights[index] += ( weightByAlpha ? w : 1.0f );
- m_remap[i] = index;
- break;
- }
- }
- }
-
- // square root the weights
- for( int i = 0; i < m_count; ++i )
- m_weights[i] = std::sqrt( m_weights[i] );
-}
-
-void ColourSet::RemapIndices( u8 const* source, u8* target ) const
-{
- for( int i = 0; i < 16; ++i )
- {
- int j = m_remap[i];
- if( j == -1 )
- target[i] = 3;
- else
- target[i] = source[j];
- }
-}
-
-} // namespace squish
diff --git a/thirdparty/squish/colourset.h b/thirdparty/squish/colourset.h
deleted file mode 100644
index e13bb6fc35..0000000000
--- a/thirdparty/squish/colourset.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/* -----------------------------------------------------------------------------
-
- Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
-
- 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 SQUISH_COLOURSET_H
-#define SQUISH_COLOURSET_H
-
-#include "squish.h"
-#include "maths.h"
-
-namespace squish {
-
-/*! @brief Represents a set of block colours
-*/
-class ColourSet
-{
-public:
- ColourSet( u8 const* rgba, int mask, int flags );
-
- int GetCount() const { return m_count; }
- Vec3 const* GetPoints() const { return m_points; }
- float const* GetWeights() const { return m_weights; }
- bool IsTransparent() const { return m_transparent; }
-
- void RemapIndices( u8 const* source, u8* target ) const;
-
-private:
- int m_count;
- Vec3 m_points[16];
- float m_weights[16];
- int m_remap[16];
- bool m_transparent;
-};
-
-} // namespace sqish
-
-#endif // ndef SQUISH_COLOURSET_H
diff --git a/thirdparty/squish/config.h b/thirdparty/squish/config.h
deleted file mode 100644
index 05f8d72598..0000000000
--- a/thirdparty/squish/config.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/* -----------------------------------------------------------------------------
-
- Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
-
- 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 SQUISH_CONFIG_H
-#define SQUISH_CONFIG_H
-
-// Set to 1 when building squish to use Altivec instructions.
-#ifndef SQUISH_USE_ALTIVEC
-#define SQUISH_USE_ALTIVEC 0
-#endif
-
-// Set to 1 or 2 when building squish to use SSE or SSE2 instructions.
-// -- GODOT start --
-#ifdef _MSC_VER
- #if defined(_M_IX86_FP)
- #if _M_IX86_FP >= 2
- #define SQUISH_USE_SSE 2
- #elif _M_IX86_FP >= 1
- #define SQUISH_USE_SSE 1
- #endif
- #elif defined(_M_X64)
- #define SQUISH_USE_SSE 2
- #endif
-#else
- #if defined(__SSE2__)
- #define SQUISH_USE_SSE 2
- #elif defined(__SSE__)
- #define SQUISH_USE_SSE 1
- #endif
-#endif
-// -- GODOT end --
-
-#ifndef SQUISH_USE_SSE
-#define SQUISH_USE_SSE 0
-#endif
-
-// Internally set SQUISH_USE_SIMD when either Altivec or SSE is available.
-#if SQUISH_USE_ALTIVEC && SQUISH_USE_SSE
-#error "Cannot enable both Altivec and SSE!"
-#endif
-#if SQUISH_USE_ALTIVEC || SQUISH_USE_SSE
-#define SQUISH_USE_SIMD 1
-#else
-#define SQUISH_USE_SIMD 0
-#endif
-
-#endif // ndef SQUISH_CONFIG_H
diff --git a/thirdparty/squish/maths.cpp b/thirdparty/squish/maths.cpp
deleted file mode 100644
index 4fa0bcfb35..0000000000
--- a/thirdparty/squish/maths.cpp
+++ /dev/null
@@ -1,259 +0,0 @@
-/* -----------------------------------------------------------------------------
-
- Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
-
- 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.
-
- -------------------------------------------------------------------------- */
-
-/*! @file
-
- The symmetric eigensystem solver algorithm is from
- http://www.geometrictools.com/Documentation/EigenSymmetric3x3.pdf
-*/
-
-#include "maths.h"
-#include "simd.h"
-#include <cfloat>
-
-namespace squish {
-
-Sym3x3 ComputeWeightedCovariance( int n, Vec3 const* points, float const* weights )
-{
- // compute the centroid
- float total = 0.0f;
- Vec3 centroid( 0.0f );
- for( int i = 0; i < n; ++i )
- {
- total += weights[i];
- centroid += weights[i]*points[i];
- }
- if( total > FLT_EPSILON )
- centroid /= total;
-
- // accumulate the covariance matrix
- Sym3x3 covariance( 0.0f );
- for( int i = 0; i < n; ++i )
- {
- Vec3 a = points[i] - centroid;
- Vec3 b = weights[i]*a;
-
- covariance[0] += a.X()*b.X();
- covariance[1] += a.X()*b.Y();
- covariance[2] += a.X()*b.Z();
- covariance[3] += a.Y()*b.Y();
- covariance[4] += a.Y()*b.Z();
- covariance[5] += a.Z()*b.Z();
- }
-
- // return it
- return covariance;
-}
-
-#if 0
-
-static Vec3 GetMultiplicity1Evector( Sym3x3 const& matrix, float evalue )
-{
- // compute M
- Sym3x3 m;
- m[0] = matrix[0] - evalue;
- m[1] = matrix[1];
- m[2] = matrix[2];
- m[3] = matrix[3] - evalue;
- m[4] = matrix[4];
- m[5] = matrix[5] - evalue;
-
- // compute U
- Sym3x3 u;
- u[0] = m[3]*m[5] - m[4]*m[4];
- u[1] = m[2]*m[4] - m[1]*m[5];
- u[2] = m[1]*m[4] - m[2]*m[3];
- u[3] = m[0]*m[5] - m[2]*m[2];
- u[4] = m[1]*m[2] - m[4]*m[0];
- u[5] = m[0]*m[3] - m[1]*m[1];
-
- // find the largest component
- float mc = std::fabs( u[0] );
- int mi = 0;
- for( int i = 1; i < 6; ++i )
- {
- float c = std::fabs( u[i] );
- if( c > mc )
- {
- mc = c;
- mi = i;
- }
- }
-
- // pick the column with this component
- switch( mi )
- {
- case 0:
- return Vec3( u[0], u[1], u[2] );
-
- case 1:
- case 3:
- return Vec3( u[1], u[3], u[4] );
-
- default:
- return Vec3( u[2], u[4], u[5] );
- }
-}
-
-static Vec3 GetMultiplicity2Evector( Sym3x3 const& matrix, float evalue )
-{
- // compute M
- Sym3x3 m;
- m[0] = matrix[0] - evalue;
- m[1] = matrix[1];
- m[2] = matrix[2];
- m[3] = matrix[3] - evalue;
- m[4] = matrix[4];
- m[5] = matrix[5] - evalue;
-
- // find the largest component
- float mc = std::fabs( m[0] );
- int mi = 0;
- for( int i = 1; i < 6; ++i )
- {
- float c = std::fabs( m[i] );
- if( c > mc )
- {
- mc = c;
- mi = i;
- }
- }
-
- // pick the first eigenvector based on this index
- switch( mi )
- {
- case 0:
- case 1:
- return Vec3( -m[1], m[0], 0.0f );
-
- case 2:
- return Vec3( m[2], 0.0f, -m[0] );
-
- case 3:
- case 4:
- return Vec3( 0.0f, -m[4], m[3] );
-
- default:
- return Vec3( 0.0f, -m[5], m[4] );
- }
-}
-
-Vec3 ComputePrincipleComponent( Sym3x3 const& matrix )
-{
- // compute the cubic coefficients
- float c0 = matrix[0]*matrix[3]*matrix[5]
- + 2.0f*matrix[1]*matrix[2]*matrix[4]
- - matrix[0]*matrix[4]*matrix[4]
- - matrix[3]*matrix[2]*matrix[2]
- - matrix[5]*matrix[1]*matrix[1];
- float c1 = matrix[0]*matrix[3] + matrix[0]*matrix[5] + matrix[3]*matrix[5]
- - matrix[1]*matrix[1] - matrix[2]*matrix[2] - matrix[4]*matrix[4];
- float c2 = matrix[0] + matrix[3] + matrix[5];
-
- // compute the quadratic coefficients
- float a = c1 - ( 1.0f/3.0f )*c2*c2;
- float b = ( -2.0f/27.0f )*c2*c2*c2 + ( 1.0f/3.0f )*c1*c2 - c0;
-
- // compute the root count check
- float Q = 0.25f*b*b + ( 1.0f/27.0f )*a*a*a;
-
- // test the multiplicity
- if( FLT_EPSILON < Q )
- {
- // only one root, which implies we have a multiple of the identity
- return Vec3( 1.0f );
- }
- else if( Q < -FLT_EPSILON )
- {
- // three distinct roots
- float theta = std::atan2( std::sqrt( -Q ), -0.5f*b );
- float rho = std::sqrt( 0.25f*b*b - Q );
-
- float rt = std::pow( rho, 1.0f/3.0f );
- float ct = std::cos( theta/3.0f );
- float st = std::sin( theta/3.0f );
-
- float l1 = ( 1.0f/3.0f )*c2 + 2.0f*rt*ct;
- float l2 = ( 1.0f/3.0f )*c2 - rt*( ct + ( float )sqrt( 3.0f )*st );
- float l3 = ( 1.0f/3.0f )*c2 - rt*( ct - ( float )sqrt( 3.0f )*st );
-
- // pick the larger
- if( std::fabs( l2 ) > std::fabs( l1 ) )
- l1 = l2;
- if( std::fabs( l3 ) > std::fabs( l1 ) )
- l1 = l3;
-
- // get the eigenvector
- return GetMultiplicity1Evector( matrix, l1 );
- }
- else // if( -FLT_EPSILON <= Q && Q <= FLT_EPSILON )
- {
- // two roots
- float rt;
- if( b < 0.0f )
- rt = -std::pow( -0.5f*b, 1.0f/3.0f );
- else
- rt = std::pow( 0.5f*b, 1.0f/3.0f );
-
- float l1 = ( 1.0f/3.0f )*c2 + rt; // repeated
- float l2 = ( 1.0f/3.0f )*c2 - 2.0f*rt;
-
- // get the eigenvector
- if( std::fabs( l1 ) > std::fabs( l2 ) )
- return GetMultiplicity2Evector( matrix, l1 );
- else
- return GetMultiplicity1Evector( matrix, l2 );
- }
-}
-
-#else
-
-#define POWER_ITERATION_COUNT 8
-
-Vec3 ComputePrincipleComponent( Sym3x3 const& matrix )
-{
- Vec4 const row0( matrix[0], matrix[1], matrix[2], 0.0f );
- Vec4 const row1( matrix[1], matrix[3], matrix[4], 0.0f );
- Vec4 const row2( matrix[2], matrix[4], matrix[5], 0.0f );
- Vec4 v = VEC4_CONST( 1.0f );
- for( int i = 0; i < POWER_ITERATION_COUNT; ++i )
- {
- // matrix multiply
- Vec4 w = row0*v.SplatX();
- w = MultiplyAdd(row1, v.SplatY(), w);
- w = MultiplyAdd(row2, v.SplatZ(), w);
-
- // get max component from xyz in all channels
- Vec4 a = Max(w.SplatX(), Max(w.SplatY(), w.SplatZ()));
-
- // divide through and advance
- v = w*Reciprocal(a);
- }
- return v.GetVec3();
-}
-
-#endif
-
-} // namespace squish
diff --git a/thirdparty/squish/maths.h b/thirdparty/squish/maths.h
deleted file mode 100644
index 59c32196b1..0000000000
--- a/thirdparty/squish/maths.h
+++ /dev/null
@@ -1,233 +0,0 @@
-/* -----------------------------------------------------------------------------
-
- Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
-
- 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 SQUISH_MATHS_H
-#define SQUISH_MATHS_H
-
-#include <cmath>
-#include <algorithm>
-#include "config.h"
-
-namespace squish {
-
-class Vec3
-{
-public:
- typedef Vec3 const& Arg;
-
- Vec3()
- {
- }
-
- explicit Vec3( float s )
- {
- m_x = s;
- m_y = s;
- m_z = s;
- }
-
- Vec3( float x, float y, float z )
- {
- m_x = x;
- m_y = y;
- m_z = z;
- }
-
- float X() const { return m_x; }
- float Y() const { return m_y; }
- float Z() const { return m_z; }
-
- Vec3 operator-() const
- {
- return Vec3( -m_x, -m_y, -m_z );
- }
-
- Vec3& operator+=( Arg v )
- {
- m_x += v.m_x;
- m_y += v.m_y;
- m_z += v.m_z;
- return *this;
- }
-
- Vec3& operator-=( Arg v )
- {
- m_x -= v.m_x;
- m_y -= v.m_y;
- m_z -= v.m_z;
- return *this;
- }
-
- Vec3& operator*=( Arg v )
- {
- m_x *= v.m_x;
- m_y *= v.m_y;
- m_z *= v.m_z;
- return *this;
- }
-
- Vec3& operator*=( float s )
- {
- m_x *= s;
- m_y *= s;
- m_z *= s;
- return *this;
- }
-
- Vec3& operator/=( Arg v )
- {
- m_x /= v.m_x;
- m_y /= v.m_y;
- m_z /= v.m_z;
- return *this;
- }
-
- Vec3& operator/=( float s )
- {
- float t = 1.0f/s;
- m_x *= t;
- m_y *= t;
- m_z *= t;
- return *this;
- }
-
- friend Vec3 operator+( Arg left, Arg right )
- {
- Vec3 copy( left );
- return copy += right;
- }
-
- friend Vec3 operator-( Arg left, Arg right )
- {
- Vec3 copy( left );
- return copy -= right;
- }
-
- friend Vec3 operator*( Arg left, Arg right )
- {
- Vec3 copy( left );
- return copy *= right;
- }
-
- friend Vec3 operator*( Arg left, float right )
- {
- Vec3 copy( left );
- return copy *= right;
- }
-
- friend Vec3 operator*( float left, Arg right )
- {
- Vec3 copy( right );
- return copy *= left;
- }
-
- friend Vec3 operator/( Arg left, Arg right )
- {
- Vec3 copy( left );
- return copy /= right;
- }
-
- friend Vec3 operator/( Arg left, float right )
- {
- Vec3 copy( left );
- return copy /= right;
- }
-
- friend float Dot( Arg left, Arg right )
- {
- return left.m_x*right.m_x + left.m_y*right.m_y + left.m_z*right.m_z;
- }
-
- friend Vec3 Min( Arg left, Arg right )
- {
- return Vec3(
- std::min( left.m_x, right.m_x ),
- std::min( left.m_y, right.m_y ),
- std::min( left.m_z, right.m_z )
- );
- }
-
- friend Vec3 Max( Arg left, Arg right )
- {
- return Vec3(
- std::max( left.m_x, right.m_x ),
- std::max( left.m_y, right.m_y ),
- std::max( left.m_z, right.m_z )
- );
- }
-
- friend Vec3 Truncate( Arg v )
- {
- return Vec3(
- v.m_x > 0.0f ? std::floor( v.m_x ) : std::ceil( v.m_x ),
- v.m_y > 0.0f ? std::floor( v.m_y ) : std::ceil( v.m_y ),
- v.m_z > 0.0f ? std::floor( v.m_z ) : std::ceil( v.m_z )
- );
- }
-
-private:
- float m_x;
- float m_y;
- float m_z;
-};
-
-inline float LengthSquared( Vec3::Arg v )
-{
- return Dot( v, v );
-}
-
-class Sym3x3
-{
-public:
- Sym3x3()
- {
- }
-
- Sym3x3( float s )
- {
- for( int i = 0; i < 6; ++i )
- m_x[i] = s;
- }
-
- float operator[]( int index ) const
- {
- return m_x[index];
- }
-
- float& operator[]( int index )
- {
- return m_x[index];
- }
-
-private:
- float m_x[6];
-};
-
-Sym3x3 ComputeWeightedCovariance( int n, Vec3 const* points, float const* weights );
-Vec3 ComputePrincipleComponent( Sym3x3 const& matrix );
-
-} // namespace squish
-
-#endif // ndef SQUISH_MATHS_H
diff --git a/thirdparty/squish/patches/config_sse.patch b/thirdparty/squish/patches/config_sse.patch
deleted file mode 100644
index 047701ee32..0000000000
--- a/thirdparty/squish/patches/config_sse.patch
+++ /dev/null
@@ -1,31 +0,0 @@
-diff --git a/thirdparty/squish/config.h b/thirdparty/squish/config.h
-index 92edefe966..05f8d72598 100644
---- a/thirdparty/squish/config.h
-+++ b/thirdparty/squish/config.h
-@@ -32,6 +32,26 @@
- #endif
-
- // Set to 1 or 2 when building squish to use SSE or SSE2 instructions.
-+// -- GODOT start --
-+#ifdef _MSC_VER
-+ #if defined(_M_IX86_FP)
-+ #if _M_IX86_FP >= 2
-+ #define SQUISH_USE_SSE 2
-+ #elif _M_IX86_FP >= 1
-+ #define SQUISH_USE_SSE 1
-+ #endif
-+ #elif defined(_M_X64)
-+ #define SQUISH_USE_SSE 2
-+ #endif
-+#else
-+ #if defined(__SSE2__)
-+ #define SQUISH_USE_SSE 2
-+ #elif defined(__SSE__)
-+ #define SQUISH_USE_SSE 1
-+ #endif
-+#endif
-+// -- GODOT end --
-+
- #ifndef SQUISH_USE_SSE
- #define SQUISH_USE_SSE 0
- #endif
diff --git a/thirdparty/squish/patches/decompress_bc4_bc5.patch b/thirdparty/squish/patches/decompress_bc4_bc5.patch
deleted file mode 100644
index 949375560c..0000000000
--- a/thirdparty/squish/patches/decompress_bc4_bc5.patch
+++ /dev/null
@@ -1,85 +0,0 @@
-diff --git a/thirdparty/squish/colourblock.cpp b/thirdparty/squish/colourblock.cpp
-index af8b980365..f14c9362bd 100644
---- a/thirdparty/squish/colourblock.cpp
-+++ b/thirdparty/squish/colourblock.cpp
-@@ -24,6 +24,9 @@
- -------------------------------------------------------------------------- */
-
- #include "colourblock.h"
-+// -- GODOT start --
-+#include "alpha.h"
-+// -- GODOT end --
-
- namespace squish {
-
-@@ -211,4 +214,34 @@ void DecompressColour( u8* rgba, void const* block, bool isDxt1 )
- }
- }
-
-+// -- GODOT start --
-+void DecompressColourBc4( u8* rgba, void const* block)
-+{
-+ DecompressAlphaDxt5(rgba,block);
-+ for ( int i = 0; i < 16; ++i ) {
-+ rgba[i*4] = rgba[i*4 + 3];
-+ rgba[i*4 + 1] = 0;
-+ rgba[i*4 + 2] = 0;
-+ rgba[i*4 + 3] = 255;
-+ }
-+}
-+
-+void DecompressColourBc5( u8* rgba, void const* block)
-+{
-+ void const* rblock = block;
-+ void const* gblock = reinterpret_cast< u8 const* >( block ) + 8;
-+ DecompressAlphaDxt5(rgba,rblock);
-+ for ( int i = 0; i < 16; ++i ) {
-+ rgba[i*4] = rgba[i*4 + 3];
-+ }
-+ DecompressAlphaDxt5(rgba,gblock);
-+ for ( int i = 0; i < 16; ++i ) {
-+ rgba[i*4+1] = rgba[i*4 + 3];
-+ rgba[i*4 + 2] = 0;
-+ rgba[i*4 + 3] = 255;
-+ }
-+}
-+// -- GODOT end --
-+
-+
- } // namespace squish
-diff --git a/thirdparty/squish/colourblock.h b/thirdparty/squish/colourblock.h
-index fee2cd7c5d..e1eb9e4917 100644
---- a/thirdparty/squish/colourblock.h
-+++ b/thirdparty/squish/colourblock.h
-@@ -35,6 +35,10 @@ void WriteColourBlock3( Vec3::Arg start, Vec3::Arg end, u8 const* indices, void*
- void WriteColourBlock4( Vec3::Arg start, Vec3::Arg end, u8 const* indices, void* block );
-
- void DecompressColour( u8* rgba, void const* block, bool isDxt1 );
-+// -- GODOT start --
-+void DecompressColourBc4( u8* rgba, void const* block );
-+void DecompressColourBc5( u8* rgba, void const* block );
-+// -- GODOT end --
-
- } // namespace squish
-
-diff --git a/thirdparty/squish/squish.cpp b/thirdparty/squish/squish.cpp
-index 1d22a64ad6..086ba11cd0 100644
---- a/thirdparty/squish/squish.cpp
-+++ b/thirdparty/squish/squish.cpp
-@@ -135,7 +135,15 @@ void Decompress( u8* rgba, void const* block, int flags )
- colourBlock = reinterpret_cast< u8 const* >( block ) + 8;
-
- // decompress colour
-- DecompressColour( rgba, colourBlock, ( flags & kDxt1 ) != 0 );
-+ // -- GODOT start --
-+ //DecompressColour( rgba, colourBlock, ( flags & kDxt1 ) != 0 );
-+ if(( flags & ( kBc4 ) ) != 0)
-+ DecompressColourBc4( rgba, colourBlock);
-+ else if(( flags & ( kBc5 ) ) != 0)
-+ DecompressColourBc5( rgba, colourBlock);
-+ else
-+ DecompressColour( rgba, colourBlock, ( flags & kDxt1 ) != 0 );
-+ // -- GODOT end --
-
- // decompress alpha separately if necessary
- if( ( flags & kDxt3 ) != 0 )
diff --git a/thirdparty/squish/rangefit.cpp b/thirdparty/squish/rangefit.cpp
deleted file mode 100644
index adc07ed7d2..0000000000
--- a/thirdparty/squish/rangefit.cpp
+++ /dev/null
@@ -1,201 +0,0 @@
-/* -----------------------------------------------------------------------------
-
- Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
-
- 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 "rangefit.h"
-#include "colourset.h"
-#include "colourblock.h"
-#include <cfloat>
-
-namespace squish {
-
-RangeFit::RangeFit( ColourSet const* colours, int flags, float* metric )
- : ColourFit( colours, flags )
-{
- // initialise the metric (old perceptual = 0.2126f, 0.7152f, 0.0722f)
- if( metric )
- m_metric = Vec3( metric[0], metric[1], metric[2] );
- else
- m_metric = Vec3( 1.0f );
-
- // initialise the best error
- m_besterror = FLT_MAX;
-
- // cache some values
- int const count = m_colours->GetCount();
- Vec3 const* values = m_colours->GetPoints();
- float const* weights = m_colours->GetWeights();
-
- // get the covariance matrix
- Sym3x3 covariance = ComputeWeightedCovariance( count, values, weights );
-
- // compute the principle component
- Vec3 principle = ComputePrincipleComponent( covariance );
-
- // get the min and max range as the codebook endpoints
- Vec3 start( 0.0f );
- Vec3 end( 0.0f );
- if( count > 0 )
- {
- float min, max;
-
- // compute the range
- start = end = values[0];
- min = max = Dot( values[0], principle );
- for( int i = 1; i < count; ++i )
- {
- float val = Dot( values[i], principle );
- if( val < min )
- {
- start = values[i];
- min = val;
- }
- else if( val > max )
- {
- end = values[i];
- max = val;
- }
- }
- }
-
- // clamp the output to [0, 1]
- Vec3 const one( 1.0f );
- Vec3 const zero( 0.0f );
- start = Min( one, Max( zero, start ) );
- end = Min( one, Max( zero, end ) );
-
- // clamp to the grid and save
- Vec3 const grid( 31.0f, 63.0f, 31.0f );
- Vec3 const gridrcp( 1.0f/31.0f, 1.0f/63.0f, 1.0f/31.0f );
- Vec3 const half( 0.5f );
- m_start = Truncate( grid*start + half )*gridrcp;
- m_end = Truncate( grid*end + half )*gridrcp;
-}
-
-void RangeFit::Compress3( void* block )
-{
- // cache some values
- int const count = m_colours->GetCount();
- Vec3 const* values = m_colours->GetPoints();
-
- // create a codebook
- Vec3 codes[3];
- codes[0] = m_start;
- codes[1] = m_end;
- codes[2] = 0.5f*m_start + 0.5f*m_end;
-
- // match each point to the closest code
- u8 closest[16];
- float error = 0.0f;
- for( int i = 0; i < count; ++i )
- {
- // find the closest code
- float dist = FLT_MAX;
- int idx = 0;
- for( int j = 0; j < 3; ++j )
- {
- float d = LengthSquared( m_metric*( values[i] - codes[j] ) );
- if( d < dist )
- {
- dist = d;
- idx = j;
- }
- }
-
- // save the index
- closest[i] = ( u8 )idx;
-
- // accumulate the error
- error += dist;
- }
-
- // save this scheme if it wins
- if( error < m_besterror )
- {
- // remap the indices
- u8 indices[16];
- m_colours->RemapIndices( closest, indices );
-
- // save the block
- WriteColourBlock3( m_start, m_end, indices, block );
-
- // save the error
- m_besterror = error;
- }
-}
-
-void RangeFit::Compress4( void* block )
-{
- // cache some values
- int const count = m_colours->GetCount();
- Vec3 const* values = m_colours->GetPoints();
-
- // create a codebook
- Vec3 codes[4];
- codes[0] = m_start;
- codes[1] = m_end;
- codes[2] = ( 2.0f/3.0f )*m_start + ( 1.0f/3.0f )*m_end;
- codes[3] = ( 1.0f/3.0f )*m_start + ( 2.0f/3.0f )*m_end;
-
- // match each point to the closest code
- u8 closest[16];
- float error = 0.0f;
- for( int i = 0; i < count; ++i )
- {
- // find the closest code
- float dist = FLT_MAX;
- int idx = 0;
- for( int j = 0; j < 4; ++j )
- {
- float d = LengthSquared( m_metric*( values[i] - codes[j] ) );
- if( d < dist )
- {
- dist = d;
- idx = j;
- }
- }
-
- // save the index
- closest[i] = ( u8 )idx;
-
- // accumulate the error
- error += dist;
- }
-
- // save this scheme if it wins
- if( error < m_besterror )
- {
- // remap the indices
- u8 indices[16];
- m_colours->RemapIndices( closest, indices );
-
- // save the block
- WriteColourBlock4( m_start, m_end, indices, block );
-
- // save the error
- m_besterror = error;
- }
-}
-
-} // namespace squish
diff --git a/thirdparty/squish/rangefit.h b/thirdparty/squish/rangefit.h
deleted file mode 100644
index bdb21a9007..0000000000
--- a/thirdparty/squish/rangefit.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/* -----------------------------------------------------------------------------
-
- Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
-
- 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 SQUISH_RANGEFIT_H
-#define SQUISH_RANGEFIT_H
-
-#include "squish.h"
-#include "colourfit.h"
-#include "maths.h"
-
-namespace squish {
-
-class ColourSet;
-
-class RangeFit : public ColourFit
-{
-public:
- RangeFit( ColourSet const* colours, int flags, float* metric );
-
-private:
- virtual void Compress3( void* block );
- virtual void Compress4( void* block );
-
- Vec3 m_metric;
- Vec3 m_start;
- Vec3 m_end;
- float m_besterror;
-};
-
-} // squish
-
-#endif // ndef SQUISH_RANGEFIT_H
diff --git a/thirdparty/squish/simd.h b/thirdparty/squish/simd.h
deleted file mode 100644
index 1e02fa160e..0000000000
--- a/thirdparty/squish/simd.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/* -----------------------------------------------------------------------------
-
- Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
-
- 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 SQUISH_SIMD_H
-#define SQUISH_SIMD_H
-
-#include "maths.h"
-
-#if SQUISH_USE_ALTIVEC
-#include "simd_ve.h"
-#elif SQUISH_USE_SSE
-#include "simd_sse.h"
-#else
-#include "simd_float.h"
-#endif
-
-
-#endif // ndef SQUISH_SIMD_H
diff --git a/thirdparty/squish/simd_float.h b/thirdparty/squish/simd_float.h
deleted file mode 100644
index 030ea70950..0000000000
--- a/thirdparty/squish/simd_float.h
+++ /dev/null
@@ -1,183 +0,0 @@
-/* -----------------------------------------------------------------------------
-
- Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
-
- 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 SQUISH_SIMD_FLOAT_H
-#define SQUISH_SIMD_FLOAT_H
-
-#include <algorithm>
-
-namespace squish {
-
-#define VEC4_CONST( X ) Vec4( X )
-
-class Vec4
-{
-public:
- typedef Vec4 const& Arg;
-
- Vec4() {}
-
- explicit Vec4( float s )
- : m_x( s ),
- m_y( s ),
- m_z( s ),
- m_w( s )
- {
- }
-
- Vec4( float x, float y, float z, float w )
- : m_x( x ),
- m_y( y ),
- m_z( z ),
- m_w( w )
- {
- }
-
- Vec3 GetVec3() const
- {
- return Vec3( m_x, m_y, m_z );
- }
-
- Vec4 SplatX() const { return Vec4( m_x ); }
- Vec4 SplatY() const { return Vec4( m_y ); }
- Vec4 SplatZ() const { return Vec4( m_z ); }
- Vec4 SplatW() const { return Vec4( m_w ); }
-
- Vec4& operator+=( Arg v )
- {
- m_x += v.m_x;
- m_y += v.m_y;
- m_z += v.m_z;
- m_w += v.m_w;
- return *this;
- }
-
- Vec4& operator-=( Arg v )
- {
- m_x -= v.m_x;
- m_y -= v.m_y;
- m_z -= v.m_z;
- m_w -= v.m_w;
- return *this;
- }
-
- Vec4& operator*=( Arg v )
- {
- m_x *= v.m_x;
- m_y *= v.m_y;
- m_z *= v.m_z;
- m_w *= v.m_w;
- return *this;
- }
-
- friend Vec4 operator+( Vec4::Arg left, Vec4::Arg right )
- {
- Vec4 copy( left );
- return copy += right;
- }
-
- friend Vec4 operator-( Vec4::Arg left, Vec4::Arg right )
- {
- Vec4 copy( left );
- return copy -= right;
- }
-
- friend Vec4 operator*( Vec4::Arg left, Vec4::Arg right )
- {
- Vec4 copy( left );
- return copy *= right;
- }
-
- //! Returns a*b + c
- friend Vec4 MultiplyAdd( Vec4::Arg a, Vec4::Arg b, Vec4::Arg c )
- {
- return a*b + c;
- }
-
- //! Returns -( a*b - c )
- friend Vec4 NegativeMultiplySubtract( Vec4::Arg a, Vec4::Arg b, Vec4::Arg c )
- {
- return c - a*b;
- }
-
- friend Vec4 Reciprocal( Vec4::Arg v )
- {
- return Vec4(
- 1.0f/v.m_x,
- 1.0f/v.m_y,
- 1.0f/v.m_z,
- 1.0f/v.m_w
- );
- }
-
- friend Vec4 Min( Vec4::Arg left, Vec4::Arg right )
- {
- return Vec4(
- std::min( left.m_x, right.m_x ),
- std::min( left.m_y, right.m_y ),
- std::min( left.m_z, right.m_z ),
- std::min( left.m_w, right.m_w )
- );
- }
-
- friend Vec4 Max( Vec4::Arg left, Vec4::Arg right )
- {
- return Vec4(
- std::max( left.m_x, right.m_x ),
- std::max( left.m_y, right.m_y ),
- std::max( left.m_z, right.m_z ),
- std::max( left.m_w, right.m_w )
- );
- }
-
- friend Vec4 Truncate( Vec4::Arg v )
- {
- return Vec4(
- v.m_x > 0.0f ? std::floor( v.m_x ) : std::ceil( v.m_x ),
- v.m_y > 0.0f ? std::floor( v.m_y ) : std::ceil( v.m_y ),
- v.m_z > 0.0f ? std::floor( v.m_z ) : std::ceil( v.m_z ),
- v.m_w > 0.0f ? std::floor( v.m_w ) : std::ceil( v.m_w )
- );
- }
-
- friend bool CompareAnyLessThan( Vec4::Arg left, Vec4::Arg right )
- {
- return left.m_x < right.m_x
- || left.m_y < right.m_y
- || left.m_z < right.m_z
- || left.m_w < right.m_w;
- }
-
-private:
- float m_x;
- float m_y;
- float m_z;
- float m_w;
-};
-
-} // namespace squish
-
-#endif // ndef SQUISH_SIMD_FLOAT_H
-
diff --git a/thirdparty/squish/simd_sse.h b/thirdparty/squish/simd_sse.h
deleted file mode 100644
index 2e8be4ca7b..0000000000
--- a/thirdparty/squish/simd_sse.h
+++ /dev/null
@@ -1,180 +0,0 @@
-/* -----------------------------------------------------------------------------
-
- Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
-
- 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 SQUISH_SIMD_SSE_H
-#define SQUISH_SIMD_SSE_H
-
-#include <xmmintrin.h>
-#if ( SQUISH_USE_SSE > 1 )
-#include <emmintrin.h>
-#endif
-
-#define SQUISH_SSE_SPLAT( a ) \
- ( ( a ) | ( ( a ) << 2 ) | ( ( a ) << 4 ) | ( ( a ) << 6 ) )
-
-#define SQUISH_SSE_SHUF( x, y, z, w ) \
- ( ( x ) | ( ( y ) << 2 ) | ( ( z ) << 4 ) | ( ( w ) << 6 ) )
-
-namespace squish {
-
-#define VEC4_CONST( X ) Vec4( X )
-
-class Vec4
-{
-public:
- typedef Vec4 const& Arg;
-
- Vec4() {}
-
- explicit Vec4( __m128 v ) : m_v( v ) {}
-
- Vec4( Vec4 const& arg ) : m_v( arg.m_v ) {}
-
- Vec4& operator=( Vec4 const& arg )
- {
- m_v = arg.m_v;
- return *this;
- }
-
- explicit Vec4( float s ) : m_v( _mm_set1_ps( s ) ) {}
-
- Vec4( float x, float y, float z, float w ) : m_v( _mm_setr_ps( x, y, z, w ) ) {}
-
- Vec3 GetVec3() const
- {
-#ifdef __GNUC__
- __attribute__ ((__aligned__ (16))) float c[4];
-#else
- __declspec(align(16)) float c[4];
-#endif
- _mm_store_ps( c, m_v );
- return Vec3( c[0], c[1], c[2] );
- }
-
- Vec4 SplatX() const { return Vec4( _mm_shuffle_ps( m_v, m_v, SQUISH_SSE_SPLAT( 0 ) ) ); }
- Vec4 SplatY() const { return Vec4( _mm_shuffle_ps( m_v, m_v, SQUISH_SSE_SPLAT( 1 ) ) ); }
- Vec4 SplatZ() const { return Vec4( _mm_shuffle_ps( m_v, m_v, SQUISH_SSE_SPLAT( 2 ) ) ); }
- Vec4 SplatW() const { return Vec4( _mm_shuffle_ps( m_v, m_v, SQUISH_SSE_SPLAT( 3 ) ) ); }
-
- Vec4& operator+=( Arg v )
- {
- m_v = _mm_add_ps( m_v, v.m_v );
- return *this;
- }
-
- Vec4& operator-=( Arg v )
- {
- m_v = _mm_sub_ps( m_v, v.m_v );
- return *this;
- }
-
- Vec4& operator*=( Arg v )
- {
- m_v = _mm_mul_ps( m_v, v.m_v );
- return *this;
- }
-
- friend Vec4 operator+( Vec4::Arg left, Vec4::Arg right )
- {
- return Vec4( _mm_add_ps( left.m_v, right.m_v ) );
- }
-
- friend Vec4 operator-( Vec4::Arg left, Vec4::Arg right )
- {
- return Vec4( _mm_sub_ps( left.m_v, right.m_v ) );
- }
-
- friend Vec4 operator*( Vec4::Arg left, Vec4::Arg right )
- {
- return Vec4( _mm_mul_ps( left.m_v, right.m_v ) );
- }
-
- //! Returns a*b + c
- friend Vec4 MultiplyAdd( Vec4::Arg a, Vec4::Arg b, Vec4::Arg c )
- {
- return Vec4( _mm_add_ps( _mm_mul_ps( a.m_v, b.m_v ), c.m_v ) );
- }
-
- //! Returns -( a*b - c )
- friend Vec4 NegativeMultiplySubtract( Vec4::Arg a, Vec4::Arg b, Vec4::Arg c )
- {
- return Vec4( _mm_sub_ps( c.m_v, _mm_mul_ps( a.m_v, b.m_v ) ) );
- }
-
- friend Vec4 Reciprocal( Vec4::Arg v )
- {
- // get the reciprocal estimate
- __m128 estimate = _mm_rcp_ps( v.m_v );
-
- // one round of Newton-Rhaphson refinement
- __m128 diff = _mm_sub_ps( _mm_set1_ps( 1.0f ), _mm_mul_ps( estimate, v.m_v ) );
- return Vec4( _mm_add_ps( _mm_mul_ps( diff, estimate ), estimate ) );
- }
-
- friend Vec4 Min( Vec4::Arg left, Vec4::Arg right )
- {
- return Vec4( _mm_min_ps( left.m_v, right.m_v ) );
- }
-
- friend Vec4 Max( Vec4::Arg left, Vec4::Arg right )
- {
- return Vec4( _mm_max_ps( left.m_v, right.m_v ) );
- }
-
- friend Vec4 Truncate( Vec4::Arg v )
- {
-#if ( SQUISH_USE_SSE == 1 )
- // convert to ints
- __m128 input = v.m_v;
- __m64 lo = _mm_cvttps_pi32( input );
- __m64 hi = _mm_cvttps_pi32( _mm_movehl_ps( input, input ) );
-
- // convert to floats
- __m128 part = _mm_movelh_ps( input, _mm_cvtpi32_ps( input, hi ) );
- __m128 truncated = _mm_cvtpi32_ps( part, lo );
-
- // clear out the MMX multimedia state to allow FP calls later
- _mm_empty();
- return Vec4( truncated );
-#else
- // use SSE2 instructions
- return Vec4( _mm_cvtepi32_ps( _mm_cvttps_epi32( v.m_v ) ) );
-#endif
- }
-
- friend bool CompareAnyLessThan( Vec4::Arg left, Vec4::Arg right )
- {
- __m128 bits = _mm_cmplt_ps( left.m_v, right.m_v );
- int value = _mm_movemask_ps( bits );
- return value != 0;
- }
-
-private:
- __m128 m_v;
-};
-
-} // namespace squish
-
-#endif // ndef SQUISH_SIMD_SSE_H
diff --git a/thirdparty/squish/simd_ve.h b/thirdparty/squish/simd_ve.h
deleted file mode 100644
index 08a1537503..0000000000
--- a/thirdparty/squish/simd_ve.h
+++ /dev/null
@@ -1,166 +0,0 @@
-/* -----------------------------------------------------------------------------
-
- Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
-
- 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 SQUISH_SIMD_VE_H
-#define SQUISH_SIMD_VE_H
-
-#include <altivec.h>
-#undef bool
-
-namespace squish {
-
-#define VEC4_CONST( X ) Vec4( ( vector float ){ X } )
-
-class Vec4
-{
-public:
- typedef Vec4 Arg;
-
- Vec4() {}
-
- explicit Vec4( vector float v ) : m_v( v ) {}
-
- Vec4( Vec4 const& arg ) : m_v( arg.m_v ) {}
-
- Vec4& operator=( Vec4 const& arg )
- {
- m_v = arg.m_v;
- return *this;
- }
-
- explicit Vec4( float s )
- {
- union { vector float v; float c[4]; } u;
- u.c[0] = s;
- u.c[1] = s;
- u.c[2] = s;
- u.c[3] = s;
- m_v = u.v;
- }
-
- Vec4( float x, float y, float z, float w )
- {
- union { vector float v; float c[4]; } u;
- u.c[0] = x;
- u.c[1] = y;
- u.c[2] = z;
- u.c[3] = w;
- m_v = u.v;
- }
-
- Vec3 GetVec3() const
- {
- union { vector float v; float c[4]; } u;
- u.v = m_v;
- return Vec3( u.c[0], u.c[1], u.c[2] );
- }
-
- Vec4 SplatX() const { return Vec4( vec_splat( m_v, 0 ) ); }
- Vec4 SplatY() const { return Vec4( vec_splat( m_v, 1 ) ); }
- Vec4 SplatZ() const { return Vec4( vec_splat( m_v, 2 ) ); }
- Vec4 SplatW() const { return Vec4( vec_splat( m_v, 3 ) ); }
-
- Vec4& operator+=( Arg v )
- {
- m_v = vec_add( m_v, v.m_v );
- return *this;
- }
-
- Vec4& operator-=( Arg v )
- {
- m_v = vec_sub( m_v, v.m_v );
- return *this;
- }
-
- Vec4& operator*=( Arg v )
- {
- m_v = vec_madd( m_v, v.m_v, ( vector float ){ -0.0f } );
- return *this;
- }
-
- friend Vec4 operator+( Vec4::Arg left, Vec4::Arg right )
- {
- return Vec4( vec_add( left.m_v, right.m_v ) );
- }
-
- friend Vec4 operator-( Vec4::Arg left, Vec4::Arg right )
- {
- return Vec4( vec_sub( left.m_v, right.m_v ) );
- }
-
- friend Vec4 operator*( Vec4::Arg left, Vec4::Arg right )
- {
- return Vec4( vec_madd( left.m_v, right.m_v, ( vector float ){ -0.0f } ) );
- }
-
- //! Returns a*b + c
- friend Vec4 MultiplyAdd( Vec4::Arg a, Vec4::Arg b, Vec4::Arg c )
- {
- return Vec4( vec_madd( a.m_v, b.m_v, c.m_v ) );
- }
-
- //! Returns -( a*b - c )
- friend Vec4 NegativeMultiplySubtract( Vec4::Arg a, Vec4::Arg b, Vec4::Arg c )
- {
- return Vec4( vec_nmsub( a.m_v, b.m_v, c.m_v ) );
- }
-
- friend Vec4 Reciprocal( Vec4::Arg v )
- {
- // get the reciprocal estimate
- vector float estimate = vec_re( v.m_v );
-
- // one round of Newton-Rhaphson refinement
- vector float diff = vec_nmsub( estimate, v.m_v, ( vector float ){ 1.0f } );
- return Vec4( vec_madd( diff, estimate, estimate ) );
- }
-
- friend Vec4 Min( Vec4::Arg left, Vec4::Arg right )
- {
- return Vec4( vec_min( left.m_v, right.m_v ) );
- }
-
- friend Vec4 Max( Vec4::Arg left, Vec4::Arg right )
- {
- return Vec4( vec_max( left.m_v, right.m_v ) );
- }
-
- friend Vec4 Truncate( Vec4::Arg v )
- {
- return Vec4( vec_trunc( v.m_v ) );
- }
-
- friend bool CompareAnyLessThan( Vec4::Arg left, Vec4::Arg right )
- {
- return vec_any_lt( left.m_v, right.m_v ) != 0;
- }
-
-private:
- vector float m_v;
-};
-
-} // namespace squish
-
-#endif // ndef SQUISH_SIMD_VE_H
diff --git a/thirdparty/squish/singlecolourfit.cpp b/thirdparty/squish/singlecolourfit.cpp
deleted file mode 100644
index cef0ebc410..0000000000
--- a/thirdparty/squish/singlecolourfit.cpp
+++ /dev/null
@@ -1,172 +0,0 @@
-/* -----------------------------------------------------------------------------
-
- Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
-
- 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 "singlecolourfit.h"
-#include "colourset.h"
-#include "colourblock.h"
-
-namespace squish {
-
-struct SourceBlock
-{
- u8 start;
- u8 end;
- u8 error;
-};
-
-struct SingleColourLookup
-{
- SourceBlock sources[2];
-};
-
-#include "singlecolourlookup.inl"
-
-static int FloatToInt( float a, int limit )
-{
- // use ANSI round-to-zero behaviour to get round-to-nearest
- int i = ( int )( a + 0.5f );
-
- // clamp to the limit
- if( i < 0 )
- i = 0;
- else if( i > limit )
- i = limit;
-
- // done
- return i;
-}
-
-SingleColourFit::SingleColourFit( ColourSet const* colours, int flags )
- : ColourFit( colours, flags )
-{
- // grab the single colour
- Vec3 const* values = m_colours->GetPoints();
- m_colour[0] = ( u8 )FloatToInt( 255.0f*values->X(), 255 );
- m_colour[1] = ( u8 )FloatToInt( 255.0f*values->Y(), 255 );
- m_colour[2] = ( u8 )FloatToInt( 255.0f*values->Z(), 255 );
-
- // initialise the best error
- m_besterror = INT_MAX;
-}
-
-void SingleColourFit::Compress3( void* block )
-{
- // build the table of lookups
- SingleColourLookup const* const lookups[] =
- {
- lookup_5_3,
- lookup_6_3,
- lookup_5_3
- };
-
- // find the best end-points and index
- ComputeEndPoints( lookups );
-
- // build the block if we win
- if( m_error < m_besterror )
- {
- // remap the indices
- u8 indices[16];
- m_colours->RemapIndices( &m_index, indices );
-
- // save the block
- WriteColourBlock3( m_start, m_end, indices, block );
-
- // save the error
- m_besterror = m_error;
- }
-}
-
-void SingleColourFit::Compress4( void* block )
-{
- // build the table of lookups
- SingleColourLookup const* const lookups[] =
- {
- lookup_5_4,
- lookup_6_4,
- lookup_5_4
- };
-
- // find the best end-points and index
- ComputeEndPoints( lookups );
-
- // build the block if we win
- if( m_error < m_besterror )
- {
- // remap the indices
- u8 indices[16];
- m_colours->RemapIndices( &m_index, indices );
-
- // save the block
- WriteColourBlock4( m_start, m_end, indices, block );
-
- // save the error
- m_besterror = m_error;
- }
-}
-
-void SingleColourFit::ComputeEndPoints( SingleColourLookup const* const* lookups )
-{
- // check each index combination (endpoint or intermediate)
- m_error = INT_MAX;
- for( int index = 0; index < 2; ++index )
- {
- // check the error for this codebook index
- SourceBlock const* sources[3];
- int error = 0;
- for( int channel = 0; channel < 3; ++channel )
- {
- // grab the lookup table and index for this channel
- SingleColourLookup const* lookup = lookups[channel];
- int target = m_colour[channel];
-
- // store a pointer to the source for this channel
- sources[channel] = lookup[target].sources + index;
-
- // accumulate the error
- int diff = sources[channel]->error;
- error += diff*diff;
- }
-
- // keep it if the error is lower
- if( error < m_error )
- {
- m_start = Vec3(
- ( float )sources[0]->start/31.0f,
- ( float )sources[1]->start/63.0f,
- ( float )sources[2]->start/31.0f
- );
- m_end = Vec3(
- ( float )sources[0]->end/31.0f,
- ( float )sources[1]->end/63.0f,
- ( float )sources[2]->end/31.0f
- );
- m_index = ( u8 )( 2*index );
- m_error = error;
- }
- }
-}
-
-} // namespace squish
diff --git a/thirdparty/squish/singlecolourfit.h b/thirdparty/squish/singlecolourfit.h
deleted file mode 100644
index 974ce77256..0000000000
--- a/thirdparty/squish/singlecolourfit.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/* -----------------------------------------------------------------------------
-
- Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
-
- 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 SQUISH_SINGLECOLOURFIT_H
-#define SQUISH_SINGLECOLOURFIT_H
-
-#include "squish.h"
-#include "colourfit.h"
-
-namespace squish {
-
-class ColourSet;
-struct SingleColourLookup;
-
-class SingleColourFit : public ColourFit
-{
-public:
- SingleColourFit( ColourSet const* colours, int flags );
-
-private:
- virtual void Compress3( void* block );
- virtual void Compress4( void* block );
-
- void ComputeEndPoints( SingleColourLookup const* const* lookups );
-
- u8 m_colour[3];
- Vec3 m_start;
- Vec3 m_end;
- u8 m_index;
- int m_error;
- int m_besterror;
-};
-
-} // namespace squish
-
-#endif // ndef SQUISH_SINGLECOLOURFIT_H
diff --git a/thirdparty/squish/singlecolourlookup.inl b/thirdparty/squish/singlecolourlookup.inl
deleted file mode 100644
index 5b44a1e5e6..0000000000
--- a/thirdparty/squish/singlecolourlookup.inl
+++ /dev/null
@@ -1,1064 +0,0 @@
-/* -----------------------------------------------------------------------------
-
- Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
-
- 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.
-
- -------------------------------------------------------------------------- */
-
-static SingleColourLookup const lookup_5_3[] =
-{
- { { { 0, 0, 0 }, { 0, 0, 0 } } },
- { { { 0, 0, 1 }, { 0, 0, 1 } } },
- { { { 0, 0, 2 }, { 0, 0, 2 } } },
- { { { 0, 0, 3 }, { 0, 1, 1 } } },
- { { { 0, 0, 4 }, { 0, 1, 0 } } },
- { { { 1, 0, 3 }, { 0, 1, 1 } } },
- { { { 1, 0, 2 }, { 0, 1, 2 } } },
- { { { 1, 0, 1 }, { 0, 2, 1 } } },
- { { { 1, 0, 0 }, { 0, 2, 0 } } },
- { { { 1, 0, 1 }, { 0, 2, 1 } } },
- { { { 1, 0, 2 }, { 0, 2, 2 } } },
- { { { 1, 0, 3 }, { 0, 3, 1 } } },
- { { { 1, 0, 4 }, { 0, 3, 0 } } },
- { { { 2, 0, 3 }, { 0, 3, 1 } } },
- { { { 2, 0, 2 }, { 0, 3, 2 } } },
- { { { 2, 0, 1 }, { 0, 4, 1 } } },
- { { { 2, 0, 0 }, { 0, 4, 0 } } },
- { { { 2, 0, 1 }, { 0, 4, 1 } } },
- { { { 2, 0, 2 }, { 0, 4, 2 } } },
- { { { 2, 0, 3 }, { 0, 5, 1 } } },
- { { { 2, 0, 4 }, { 0, 5, 0 } } },
- { { { 3, 0, 3 }, { 0, 5, 1 } } },
- { { { 3, 0, 2 }, { 0, 5, 2 } } },
- { { { 3, 0, 1 }, { 0, 6, 1 } } },
- { { { 3, 0, 0 }, { 0, 6, 0 } } },
- { { { 3, 0, 1 }, { 0, 6, 1 } } },
- { { { 3, 0, 2 }, { 0, 6, 2 } } },
- { { { 3, 0, 3 }, { 0, 7, 1 } } },
- { { { 3, 0, 4 }, { 0, 7, 0 } } },
- { { { 4, 0, 4 }, { 0, 7, 1 } } },
- { { { 4, 0, 3 }, { 0, 7, 2 } } },
- { { { 4, 0, 2 }, { 1, 7, 1 } } },
- { { { 4, 0, 1 }, { 1, 7, 0 } } },
- { { { 4, 0, 0 }, { 0, 8, 0 } } },
- { { { 4, 0, 1 }, { 0, 8, 1 } } },
- { { { 4, 0, 2 }, { 2, 7, 1 } } },
- { { { 4, 0, 3 }, { 2, 7, 0 } } },
- { { { 4, 0, 4 }, { 0, 9, 0 } } },
- { { { 5, 0, 3 }, { 0, 9, 1 } } },
- { { { 5, 0, 2 }, { 3, 7, 1 } } },
- { { { 5, 0, 1 }, { 3, 7, 0 } } },
- { { { 5, 0, 0 }, { 0, 10, 0 } } },
- { { { 5, 0, 1 }, { 0, 10, 1 } } },
- { { { 5, 0, 2 }, { 0, 10, 2 } } },
- { { { 5, 0, 3 }, { 0, 11, 1 } } },
- { { { 5, 0, 4 }, { 0, 11, 0 } } },
- { { { 6, 0, 3 }, { 0, 11, 1 } } },
- { { { 6, 0, 2 }, { 0, 11, 2 } } },
- { { { 6, 0, 1 }, { 0, 12, 1 } } },
- { { { 6, 0, 0 }, { 0, 12, 0 } } },
- { { { 6, 0, 1 }, { 0, 12, 1 } } },
- { { { 6, 0, 2 }, { 0, 12, 2 } } },
- { { { 6, 0, 3 }, { 0, 13, 1 } } },
- { { { 6, 0, 4 }, { 0, 13, 0 } } },
- { { { 7, 0, 3 }, { 0, 13, 1 } } },
- { { { 7, 0, 2 }, { 0, 13, 2 } } },
- { { { 7, 0, 1 }, { 0, 14, 1 } } },
- { { { 7, 0, 0 }, { 0, 14, 0 } } },
- { { { 7, 0, 1 }, { 0, 14, 1 } } },
- { { { 7, 0, 2 }, { 0, 14, 2 } } },
- { { { 7, 0, 3 }, { 0, 15, 1 } } },
- { { { 7, 0, 4 }, { 0, 15, 0 } } },
- { { { 8, 0, 4 }, { 0, 15, 1 } } },
- { { { 8, 0, 3 }, { 0, 15, 2 } } },
- { { { 8, 0, 2 }, { 1, 15, 1 } } },
- { { { 8, 0, 1 }, { 1, 15, 0 } } },
- { { { 8, 0, 0 }, { 0, 16, 0 } } },
- { { { 8, 0, 1 }, { 0, 16, 1 } } },
- { { { 8, 0, 2 }, { 2, 15, 1 } } },
- { { { 8, 0, 3 }, { 2, 15, 0 } } },
- { { { 8, 0, 4 }, { 0, 17, 0 } } },
- { { { 9, 0, 3 }, { 0, 17, 1 } } },
- { { { 9, 0, 2 }, { 3, 15, 1 } } },
- { { { 9, 0, 1 }, { 3, 15, 0 } } },
- { { { 9, 0, 0 }, { 0, 18, 0 } } },
- { { { 9, 0, 1 }, { 0, 18, 1 } } },
- { { { 9, 0, 2 }, { 0, 18, 2 } } },
- { { { 9, 0, 3 }, { 0, 19, 1 } } },
- { { { 9, 0, 4 }, { 0, 19, 0 } } },
- { { { 10, 0, 3 }, { 0, 19, 1 } } },
- { { { 10, 0, 2 }, { 0, 19, 2 } } },
- { { { 10, 0, 1 }, { 0, 20, 1 } } },
- { { { 10, 0, 0 }, { 0, 20, 0 } } },
- { { { 10, 0, 1 }, { 0, 20, 1 } } },
- { { { 10, 0, 2 }, { 0, 20, 2 } } },
- { { { 10, 0, 3 }, { 0, 21, 1 } } },
- { { { 10, 0, 4 }, { 0, 21, 0 } } },
- { { { 11, 0, 3 }, { 0, 21, 1 } } },
- { { { 11, 0, 2 }, { 0, 21, 2 } } },
- { { { 11, 0, 1 }, { 0, 22, 1 } } },
- { { { 11, 0, 0 }, { 0, 22, 0 } } },
- { { { 11, 0, 1 }, { 0, 22, 1 } } },
- { { { 11, 0, 2 }, { 0, 22, 2 } } },
- { { { 11, 0, 3 }, { 0, 23, 1 } } },
- { { { 11, 0, 4 }, { 0, 23, 0 } } },
- { { { 12, 0, 4 }, { 0, 23, 1 } } },
- { { { 12, 0, 3 }, { 0, 23, 2 } } },
- { { { 12, 0, 2 }, { 1, 23, 1 } } },
- { { { 12, 0, 1 }, { 1, 23, 0 } } },
- { { { 12, 0, 0 }, { 0, 24, 0 } } },
- { { { 12, 0, 1 }, { 0, 24, 1 } } },
- { { { 12, 0, 2 }, { 2, 23, 1 } } },
- { { { 12, 0, 3 }, { 2, 23, 0 } } },
- { { { 12, 0, 4 }, { 0, 25, 0 } } },
- { { { 13, 0, 3 }, { 0, 25, 1 } } },
- { { { 13, 0, 2 }, { 3, 23, 1 } } },
- { { { 13, 0, 1 }, { 3, 23, 0 } } },
- { { { 13, 0, 0 }, { 0, 26, 0 } } },
- { { { 13, 0, 1 }, { 0, 26, 1 } } },
- { { { 13, 0, 2 }, { 0, 26, 2 } } },
- { { { 13, 0, 3 }, { 0, 27, 1 } } },
- { { { 13, 0, 4 }, { 0, 27, 0 } } },
- { { { 14, 0, 3 }, { 0, 27, 1 } } },
- { { { 14, 0, 2 }, { 0, 27, 2 } } },
- { { { 14, 0, 1 }, { 0, 28, 1 } } },
- { { { 14, 0, 0 }, { 0, 28, 0 } } },
- { { { 14, 0, 1 }, { 0, 28, 1 } } },
- { { { 14, 0, 2 }, { 0, 28, 2 } } },
- { { { 14, 0, 3 }, { 0, 29, 1 } } },
- { { { 14, 0, 4 }, { 0, 29, 0 } } },
- { { { 15, 0, 3 }, { 0, 29, 1 } } },
- { { { 15, 0, 2 }, { 0, 29, 2 } } },
- { { { 15, 0, 1 }, { 0, 30, 1 } } },
- { { { 15, 0, 0 }, { 0, 30, 0 } } },
- { { { 15, 0, 1 }, { 0, 30, 1 } } },
- { { { 15, 0, 2 }, { 0, 30, 2 } } },
- { { { 15, 0, 3 }, { 0, 31, 1 } } },
- { { { 15, 0, 4 }, { 0, 31, 0 } } },
- { { { 16, 0, 4 }, { 0, 31, 1 } } },
- { { { 16, 0, 3 }, { 0, 31, 2 } } },
- { { { 16, 0, 2 }, { 1, 31, 1 } } },
- { { { 16, 0, 1 }, { 1, 31, 0 } } },
- { { { 16, 0, 0 }, { 4, 28, 0 } } },
- { { { 16, 0, 1 }, { 4, 28, 1 } } },
- { { { 16, 0, 2 }, { 2, 31, 1 } } },
- { { { 16, 0, 3 }, { 2, 31, 0 } } },
- { { { 16, 0, 4 }, { 4, 29, 0 } } },
- { { { 17, 0, 3 }, { 4, 29, 1 } } },
- { { { 17, 0, 2 }, { 3, 31, 1 } } },
- { { { 17, 0, 1 }, { 3, 31, 0 } } },
- { { { 17, 0, 0 }, { 4, 30, 0 } } },
- { { { 17, 0, 1 }, { 4, 30, 1 } } },
- { { { 17, 0, 2 }, { 4, 30, 2 } } },
- { { { 17, 0, 3 }, { 4, 31, 1 } } },
- { { { 17, 0, 4 }, { 4, 31, 0 } } },
- { { { 18, 0, 3 }, { 4, 31, 1 } } },
- { { { 18, 0, 2 }, { 4, 31, 2 } } },
- { { { 18, 0, 1 }, { 5, 31, 1 } } },
- { { { 18, 0, 0 }, { 5, 31, 0 } } },
- { { { 18, 0, 1 }, { 5, 31, 1 } } },
- { { { 18, 0, 2 }, { 5, 31, 2 } } },
- { { { 18, 0, 3 }, { 6, 31, 1 } } },
- { { { 18, 0, 4 }, { 6, 31, 0 } } },
- { { { 19, 0, 3 }, { 6, 31, 1 } } },
- { { { 19, 0, 2 }, { 6, 31, 2 } } },
- { { { 19, 0, 1 }, { 7, 31, 1 } } },
- { { { 19, 0, 0 }, { 7, 31, 0 } } },
- { { { 19, 0, 1 }, { 7, 31, 1 } } },
- { { { 19, 0, 2 }, { 7, 31, 2 } } },
- { { { 19, 0, 3 }, { 8, 31, 1 } } },
- { { { 19, 0, 4 }, { 8, 31, 0 } } },
- { { { 20, 0, 4 }, { 8, 31, 1 } } },
- { { { 20, 0, 3 }, { 8, 31, 2 } } },
- { { { 20, 0, 2 }, { 9, 31, 1 } } },
- { { { 20, 0, 1 }, { 9, 31, 0 } } },
- { { { 20, 0, 0 }, { 12, 28, 0 } } },
- { { { 20, 0, 1 }, { 12, 28, 1 } } },
- { { { 20, 0, 2 }, { 10, 31, 1 } } },
- { { { 20, 0, 3 }, { 10, 31, 0 } } },
- { { { 20, 0, 4 }, { 12, 29, 0 } } },
- { { { 21, 0, 3 }, { 12, 29, 1 } } },
- { { { 21, 0, 2 }, { 11, 31, 1 } } },
- { { { 21, 0, 1 }, { 11, 31, 0 } } },
- { { { 21, 0, 0 }, { 12, 30, 0 } } },
- { { { 21, 0, 1 }, { 12, 30, 1 } } },
- { { { 21, 0, 2 }, { 12, 30, 2 } } },
- { { { 21, 0, 3 }, { 12, 31, 1 } } },
- { { { 21, 0, 4 }, { 12, 31, 0 } } },
- { { { 22, 0, 3 }, { 12, 31, 1 } } },
- { { { 22, 0, 2 }, { 12, 31, 2 } } },
- { { { 22, 0, 1 }, { 13, 31, 1 } } },
- { { { 22, 0, 0 }, { 13, 31, 0 } } },
- { { { 22, 0, 1 }, { 13, 31, 1 } } },
- { { { 22, 0, 2 }, { 13, 31, 2 } } },
- { { { 22, 0, 3 }, { 14, 31, 1 } } },
- { { { 22, 0, 4 }, { 14, 31, 0 } } },
- { { { 23, 0, 3 }, { 14, 31, 1 } } },
- { { { 23, 0, 2 }, { 14, 31, 2 } } },
- { { { 23, 0, 1 }, { 15, 31, 1 } } },
- { { { 23, 0, 0 }, { 15, 31, 0 } } },
- { { { 23, 0, 1 }, { 15, 31, 1 } } },
- { { { 23, 0, 2 }, { 15, 31, 2 } } },
- { { { 23, 0, 3 }, { 16, 31, 1 } } },
- { { { 23, 0, 4 }, { 16, 31, 0 } } },
- { { { 24, 0, 4 }, { 16, 31, 1 } } },
- { { { 24, 0, 3 }, { 16, 31, 2 } } },
- { { { 24, 0, 2 }, { 17, 31, 1 } } },
- { { { 24, 0, 1 }, { 17, 31, 0 } } },
- { { { 24, 0, 0 }, { 20, 28, 0 } } },
- { { { 24, 0, 1 }, { 20, 28, 1 } } },
- { { { 24, 0, 2 }, { 18, 31, 1 } } },
- { { { 24, 0, 3 }, { 18, 31, 0 } } },
- { { { 24, 0, 4 }, { 20, 29, 0 } } },
- { { { 25, 0, 3 }, { 20, 29, 1 } } },
- { { { 25, 0, 2 }, { 19, 31, 1 } } },
- { { { 25, 0, 1 }, { 19, 31, 0 } } },
- { { { 25, 0, 0 }, { 20, 30, 0 } } },
- { { { 25, 0, 1 }, { 20, 30, 1 } } },
- { { { 25, 0, 2 }, { 20, 30, 2 } } },
- { { { 25, 0, 3 }, { 20, 31, 1 } } },
- { { { 25, 0, 4 }, { 20, 31, 0 } } },
- { { { 26, 0, 3 }, { 20, 31, 1 } } },
- { { { 26, 0, 2 }, { 20, 31, 2 } } },
- { { { 26, 0, 1 }, { 21, 31, 1 } } },
- { { { 26, 0, 0 }, { 21, 31, 0 } } },
- { { { 26, 0, 1 }, { 21, 31, 1 } } },
- { { { 26, 0, 2 }, { 21, 31, 2 } } },
- { { { 26, 0, 3 }, { 22, 31, 1 } } },
- { { { 26, 0, 4 }, { 22, 31, 0 } } },
- { { { 27, 0, 3 }, { 22, 31, 1 } } },
- { { { 27, 0, 2 }, { 22, 31, 2 } } },
- { { { 27, 0, 1 }, { 23, 31, 1 } } },
- { { { 27, 0, 0 }, { 23, 31, 0 } } },
- { { { 27, 0, 1 }, { 23, 31, 1 } } },
- { { { 27, 0, 2 }, { 23, 31, 2 } } },
- { { { 27, 0, 3 }, { 24, 31, 1 } } },
- { { { 27, 0, 4 }, { 24, 31, 0 } } },
- { { { 28, 0, 4 }, { 24, 31, 1 } } },
- { { { 28, 0, 3 }, { 24, 31, 2 } } },
- { { { 28, 0, 2 }, { 25, 31, 1 } } },
- { { { 28, 0, 1 }, { 25, 31, 0 } } },
- { { { 28, 0, 0 }, { 28, 28, 0 } } },
- { { { 28, 0, 1 }, { 28, 28, 1 } } },
- { { { 28, 0, 2 }, { 26, 31, 1 } } },
- { { { 28, 0, 3 }, { 26, 31, 0 } } },
- { { { 28, 0, 4 }, { 28, 29, 0 } } },
- { { { 29, 0, 3 }, { 28, 29, 1 } } },
- { { { 29, 0, 2 }, { 27, 31, 1 } } },
- { { { 29, 0, 1 }, { 27, 31, 0 } } },
- { { { 29, 0, 0 }, { 28, 30, 0 } } },
- { { { 29, 0, 1 }, { 28, 30, 1 } } },
- { { { 29, 0, 2 }, { 28, 30, 2 } } },
- { { { 29, 0, 3 }, { 28, 31, 1 } } },
- { { { 29, 0, 4 }, { 28, 31, 0 } } },
- { { { 30, 0, 3 }, { 28, 31, 1 } } },
- { { { 30, 0, 2 }, { 28, 31, 2 } } },
- { { { 30, 0, 1 }, { 29, 31, 1 } } },
- { { { 30, 0, 0 }, { 29, 31, 0 } } },
- { { { 30, 0, 1 }, { 29, 31, 1 } } },
- { { { 30, 0, 2 }, { 29, 31, 2 } } },
- { { { 30, 0, 3 }, { 30, 31, 1 } } },
- { { { 30, 0, 4 }, { 30, 31, 0 } } },
- { { { 31, 0, 3 }, { 30, 31, 1 } } },
- { { { 31, 0, 2 }, { 30, 31, 2 } } },
- { { { 31, 0, 1 }, { 31, 31, 1 } } },
- { { { 31, 0, 0 }, { 31, 31, 0 } } }
-};
-
-static SingleColourLookup const lookup_6_3[] =
-{
- { { { 0, 0, 0 }, { 0, 0, 0 } } },
- { { { 0, 0, 1 }, { 0, 1, 1 } } },
- { { { 0, 0, 2 }, { 0, 1, 0 } } },
- { { { 1, 0, 1 }, { 0, 2, 1 } } },
- { { { 1, 0, 0 }, { 0, 2, 0 } } },
- { { { 1, 0, 1 }, { 0, 3, 1 } } },
- { { { 1, 0, 2 }, { 0, 3, 0 } } },
- { { { 2, 0, 1 }, { 0, 4, 1 } } },
- { { { 2, 0, 0 }, { 0, 4, 0 } } },
- { { { 2, 0, 1 }, { 0, 5, 1 } } },
- { { { 2, 0, 2 }, { 0, 5, 0 } } },
- { { { 3, 0, 1 }, { 0, 6, 1 } } },
- { { { 3, 0, 0 }, { 0, 6, 0 } } },
- { { { 3, 0, 1 }, { 0, 7, 1 } } },
- { { { 3, 0, 2 }, { 0, 7, 0 } } },
- { { { 4, 0, 1 }, { 0, 8, 1 } } },
- { { { 4, 0, 0 }, { 0, 8, 0 } } },
- { { { 4, 0, 1 }, { 0, 9, 1 } } },
- { { { 4, 0, 2 }, { 0, 9, 0 } } },
- { { { 5, 0, 1 }, { 0, 10, 1 } } },
- { { { 5, 0, 0 }, { 0, 10, 0 } } },
- { { { 5, 0, 1 }, { 0, 11, 1 } } },
- { { { 5, 0, 2 }, { 0, 11, 0 } } },
- { { { 6, 0, 1 }, { 0, 12, 1 } } },
- { { { 6, 0, 0 }, { 0, 12, 0 } } },
- { { { 6, 0, 1 }, { 0, 13, 1 } } },
- { { { 6, 0, 2 }, { 0, 13, 0 } } },
- { { { 7, 0, 1 }, { 0, 14, 1 } } },
- { { { 7, 0, 0 }, { 0, 14, 0 } } },
- { { { 7, 0, 1 }, { 0, 15, 1 } } },
- { { { 7, 0, 2 }, { 0, 15, 0 } } },
- { { { 8, 0, 1 }, { 0, 16, 1 } } },
- { { { 8, 0, 0 }, { 0, 16, 0 } } },
- { { { 8, 0, 1 }, { 0, 17, 1 } } },
- { { { 8, 0, 2 }, { 0, 17, 0 } } },
- { { { 9, 0, 1 }, { 0, 18, 1 } } },
- { { { 9, 0, 0 }, { 0, 18, 0 } } },
- { { { 9, 0, 1 }, { 0, 19, 1 } } },
- { { { 9, 0, 2 }, { 0, 19, 0 } } },
- { { { 10, 0, 1 }, { 0, 20, 1 } } },
- { { { 10, 0, 0 }, { 0, 20, 0 } } },
- { { { 10, 0, 1 }, { 0, 21, 1 } } },
- { { { 10, 0, 2 }, { 0, 21, 0 } } },
- { { { 11, 0, 1 }, { 0, 22, 1 } } },
- { { { 11, 0, 0 }, { 0, 22, 0 } } },
- { { { 11, 0, 1 }, { 0, 23, 1 } } },
- { { { 11, 0, 2 }, { 0, 23, 0 } } },
- { { { 12, 0, 1 }, { 0, 24, 1 } } },
- { { { 12, 0, 0 }, { 0, 24, 0 } } },
- { { { 12, 0, 1 }, { 0, 25, 1 } } },
- { { { 12, 0, 2 }, { 0, 25, 0 } } },
- { { { 13, 0, 1 }, { 0, 26, 1 } } },
- { { { 13, 0, 0 }, { 0, 26, 0 } } },
- { { { 13, 0, 1 }, { 0, 27, 1 } } },
- { { { 13, 0, 2 }, { 0, 27, 0 } } },
- { { { 14, 0, 1 }, { 0, 28, 1 } } },
- { { { 14, 0, 0 }, { 0, 28, 0 } } },
- { { { 14, 0, 1 }, { 0, 29, 1 } } },
- { { { 14, 0, 2 }, { 0, 29, 0 } } },
- { { { 15, 0, 1 }, { 0, 30, 1 } } },
- { { { 15, 0, 0 }, { 0, 30, 0 } } },
- { { { 15, 0, 1 }, { 0, 31, 1 } } },
- { { { 15, 0, 2 }, { 0, 31, 0 } } },
- { { { 16, 0, 2 }, { 1, 31, 1 } } },
- { { { 16, 0, 1 }, { 1, 31, 0 } } },
- { { { 16, 0, 0 }, { 0, 32, 0 } } },
- { { { 16, 0, 1 }, { 2, 31, 0 } } },
- { { { 16, 0, 2 }, { 0, 33, 0 } } },
- { { { 17, 0, 1 }, { 3, 31, 0 } } },
- { { { 17, 0, 0 }, { 0, 34, 0 } } },
- { { { 17, 0, 1 }, { 4, 31, 0 } } },
- { { { 17, 0, 2 }, { 0, 35, 0 } } },
- { { { 18, 0, 1 }, { 5, 31, 0 } } },
- { { { 18, 0, 0 }, { 0, 36, 0 } } },
- { { { 18, 0, 1 }, { 6, 31, 0 } } },
- { { { 18, 0, 2 }, { 0, 37, 0 } } },
- { { { 19, 0, 1 }, { 7, 31, 0 } } },
- { { { 19, 0, 0 }, { 0, 38, 0 } } },
- { { { 19, 0, 1 }, { 8, 31, 0 } } },
- { { { 19, 0, 2 }, { 0, 39, 0 } } },
- { { { 20, 0, 1 }, { 9, 31, 0 } } },
- { { { 20, 0, 0 }, { 0, 40, 0 } } },
- { { { 20, 0, 1 }, { 10, 31, 0 } } },
- { { { 20, 0, 2 }, { 0, 41, 0 } } },
- { { { 21, 0, 1 }, { 11, 31, 0 } } },
- { { { 21, 0, 0 }, { 0, 42, 0 } } },
- { { { 21, 0, 1 }, { 12, 31, 0 } } },
- { { { 21, 0, 2 }, { 0, 43, 0 } } },
- { { { 22, 0, 1 }, { 13, 31, 0 } } },
- { { { 22, 0, 0 }, { 0, 44, 0 } } },
- { { { 22, 0, 1 }, { 14, 31, 0 } } },
- { { { 22, 0, 2 }, { 0, 45, 0 } } },
- { { { 23, 0, 1 }, { 15, 31, 0 } } },
- { { { 23, 0, 0 }, { 0, 46, 0 } } },
- { { { 23, 0, 1 }, { 0, 47, 1 } } },
- { { { 23, 0, 2 }, { 0, 47, 0 } } },
- { { { 24, 0, 1 }, { 0, 48, 1 } } },
- { { { 24, 0, 0 }, { 0, 48, 0 } } },
- { { { 24, 0, 1 }, { 0, 49, 1 } } },
- { { { 24, 0, 2 }, { 0, 49, 0 } } },
- { { { 25, 0, 1 }, { 0, 50, 1 } } },
- { { { 25, 0, 0 }, { 0, 50, 0 } } },
- { { { 25, 0, 1 }, { 0, 51, 1 } } },
- { { { 25, 0, 2 }, { 0, 51, 0 } } },
- { { { 26, 0, 1 }, { 0, 52, 1 } } },
- { { { 26, 0, 0 }, { 0, 52, 0 } } },
- { { { 26, 0, 1 }, { 0, 53, 1 } } },
- { { { 26, 0, 2 }, { 0, 53, 0 } } },
- { { { 27, 0, 1 }, { 0, 54, 1 } } },
- { { { 27, 0, 0 }, { 0, 54, 0 } } },
- { { { 27, 0, 1 }, { 0, 55, 1 } } },
- { { { 27, 0, 2 }, { 0, 55, 0 } } },
- { { { 28, 0, 1 }, { 0, 56, 1 } } },
- { { { 28, 0, 0 }, { 0, 56, 0 } } },
- { { { 28, 0, 1 }, { 0, 57, 1 } } },
- { { { 28, 0, 2 }, { 0, 57, 0 } } },
- { { { 29, 0, 1 }, { 0, 58, 1 } } },
- { { { 29, 0, 0 }, { 0, 58, 0 } } },
- { { { 29, 0, 1 }, { 0, 59, 1 } } },
- { { { 29, 0, 2 }, { 0, 59, 0 } } },
- { { { 30, 0, 1 }, { 0, 60, 1 } } },
- { { { 30, 0, 0 }, { 0, 60, 0 } } },
- { { { 30, 0, 1 }, { 0, 61, 1 } } },
- { { { 30, 0, 2 }, { 0, 61, 0 } } },
- { { { 31, 0, 1 }, { 0, 62, 1 } } },
- { { { 31, 0, 0 }, { 0, 62, 0 } } },
- { { { 31, 0, 1 }, { 0, 63, 1 } } },
- { { { 31, 0, 2 }, { 0, 63, 0 } } },
- { { { 32, 0, 2 }, { 1, 63, 1 } } },
- { { { 32, 0, 1 }, { 1, 63, 0 } } },
- { { { 32, 0, 0 }, { 16, 48, 0 } } },
- { { { 32, 0, 1 }, { 2, 63, 0 } } },
- { { { 32, 0, 2 }, { 16, 49, 0 } } },
- { { { 33, 0, 1 }, { 3, 63, 0 } } },
- { { { 33, 0, 0 }, { 16, 50, 0 } } },
- { { { 33, 0, 1 }, { 4, 63, 0 } } },
- { { { 33, 0, 2 }, { 16, 51, 0 } } },
- { { { 34, 0, 1 }, { 5, 63, 0 } } },
- { { { 34, 0, 0 }, { 16, 52, 0 } } },
- { { { 34, 0, 1 }, { 6, 63, 0 } } },
- { { { 34, 0, 2 }, { 16, 53, 0 } } },
- { { { 35, 0, 1 }, { 7, 63, 0 } } },
- { { { 35, 0, 0 }, { 16, 54, 0 } } },
- { { { 35, 0, 1 }, { 8, 63, 0 } } },
- { { { 35, 0, 2 }, { 16, 55, 0 } } },
- { { { 36, 0, 1 }, { 9, 63, 0 } } },
- { { { 36, 0, 0 }, { 16, 56, 0 } } },
- { { { 36, 0, 1 }, { 10, 63, 0 } } },
- { { { 36, 0, 2 }, { 16, 57, 0 } } },
- { { { 37, 0, 1 }, { 11, 63, 0 } } },
- { { { 37, 0, 0 }, { 16, 58, 0 } } },
- { { { 37, 0, 1 }, { 12, 63, 0 } } },
- { { { 37, 0, 2 }, { 16, 59, 0 } } },
- { { { 38, 0, 1 }, { 13, 63, 0 } } },
- { { { 38, 0, 0 }, { 16, 60, 0 } } },
- { { { 38, 0, 1 }, { 14, 63, 0 } } },
- { { { 38, 0, 2 }, { 16, 61, 0 } } },
- { { { 39, 0, 1 }, { 15, 63, 0 } } },
- { { { 39, 0, 0 }, { 16, 62, 0 } } },
- { { { 39, 0, 1 }, { 16, 63, 1 } } },
- { { { 39, 0, 2 }, { 16, 63, 0 } } },
- { { { 40, 0, 1 }, { 17, 63, 1 } } },
- { { { 40, 0, 0 }, { 17, 63, 0 } } },
- { { { 40, 0, 1 }, { 18, 63, 1 } } },
- { { { 40, 0, 2 }, { 18, 63, 0 } } },
- { { { 41, 0, 1 }, { 19, 63, 1 } } },
- { { { 41, 0, 0 }, { 19, 63, 0 } } },
- { { { 41, 0, 1 }, { 20, 63, 1 } } },
- { { { 41, 0, 2 }, { 20, 63, 0 } } },
- { { { 42, 0, 1 }, { 21, 63, 1 } } },
- { { { 42, 0, 0 }, { 21, 63, 0 } } },
- { { { 42, 0, 1 }, { 22, 63, 1 } } },
- { { { 42, 0, 2 }, { 22, 63, 0 } } },
- { { { 43, 0, 1 }, { 23, 63, 1 } } },
- { { { 43, 0, 0 }, { 23, 63, 0 } } },
- { { { 43, 0, 1 }, { 24, 63, 1 } } },
- { { { 43, 0, 2 }, { 24, 63, 0 } } },
- { { { 44, 0, 1 }, { 25, 63, 1 } } },
- { { { 44, 0, 0 }, { 25, 63, 0 } } },
- { { { 44, 0, 1 }, { 26, 63, 1 } } },
- { { { 44, 0, 2 }, { 26, 63, 0 } } },
- { { { 45, 0, 1 }, { 27, 63, 1 } } },
- { { { 45, 0, 0 }, { 27, 63, 0 } } },
- { { { 45, 0, 1 }, { 28, 63, 1 } } },
- { { { 45, 0, 2 }, { 28, 63, 0 } } },
- { { { 46, 0, 1 }, { 29, 63, 1 } } },
- { { { 46, 0, 0 }, { 29, 63, 0 } } },
- { { { 46, 0, 1 }, { 30, 63, 1 } } },
- { { { 46, 0, 2 }, { 30, 63, 0 } } },
- { { { 47, 0, 1 }, { 31, 63, 1 } } },
- { { { 47, 0, 0 }, { 31, 63, 0 } } },
- { { { 47, 0, 1 }, { 32, 63, 1 } } },
- { { { 47, 0, 2 }, { 32, 63, 0 } } },
- { { { 48, 0, 2 }, { 33, 63, 1 } } },
- { { { 48, 0, 1 }, { 33, 63, 0 } } },
- { { { 48, 0, 0 }, { 48, 48, 0 } } },
- { { { 48, 0, 1 }, { 34, 63, 0 } } },
- { { { 48, 0, 2 }, { 48, 49, 0 } } },
- { { { 49, 0, 1 }, { 35, 63, 0 } } },
- { { { 49, 0, 0 }, { 48, 50, 0 } } },
- { { { 49, 0, 1 }, { 36, 63, 0 } } },
- { { { 49, 0, 2 }, { 48, 51, 0 } } },
- { { { 50, 0, 1 }, { 37, 63, 0 } } },
- { { { 50, 0, 0 }, { 48, 52, 0 } } },
- { { { 50, 0, 1 }, { 38, 63, 0 } } },
- { { { 50, 0, 2 }, { 48, 53, 0 } } },
- { { { 51, 0, 1 }, { 39, 63, 0 } } },
- { { { 51, 0, 0 }, { 48, 54, 0 } } },
- { { { 51, 0, 1 }, { 40, 63, 0 } } },
- { { { 51, 0, 2 }, { 48, 55, 0 } } },
- { { { 52, 0, 1 }, { 41, 63, 0 } } },
- { { { 52, 0, 0 }, { 48, 56, 0 } } },
- { { { 52, 0, 1 }, { 42, 63, 0 } } },
- { { { 52, 0, 2 }, { 48, 57, 0 } } },
- { { { 53, 0, 1 }, { 43, 63, 0 } } },
- { { { 53, 0, 0 }, { 48, 58, 0 } } },
- { { { 53, 0, 1 }, { 44, 63, 0 } } },
- { { { 53, 0, 2 }, { 48, 59, 0 } } },
- { { { 54, 0, 1 }, { 45, 63, 0 } } },
- { { { 54, 0, 0 }, { 48, 60, 0 } } },
- { { { 54, 0, 1 }, { 46, 63, 0 } } },
- { { { 54, 0, 2 }, { 48, 61, 0 } } },
- { { { 55, 0, 1 }, { 47, 63, 0 } } },
- { { { 55, 0, 0 }, { 48, 62, 0 } } },
- { { { 55, 0, 1 }, { 48, 63, 1 } } },
- { { { 55, 0, 2 }, { 48, 63, 0 } } },
- { { { 56, 0, 1 }, { 49, 63, 1 } } },
- { { { 56, 0, 0 }, { 49, 63, 0 } } },
- { { { 56, 0, 1 }, { 50, 63, 1 } } },
- { { { 56, 0, 2 }, { 50, 63, 0 } } },
- { { { 57, 0, 1 }, { 51, 63, 1 } } },
- { { { 57, 0, 0 }, { 51, 63, 0 } } },
- { { { 57, 0, 1 }, { 52, 63, 1 } } },
- { { { 57, 0, 2 }, { 52, 63, 0 } } },
- { { { 58, 0, 1 }, { 53, 63, 1 } } },
- { { { 58, 0, 0 }, { 53, 63, 0 } } },
- { { { 58, 0, 1 }, { 54, 63, 1 } } },
- { { { 58, 0, 2 }, { 54, 63, 0 } } },
- { { { 59, 0, 1 }, { 55, 63, 1 } } },
- { { { 59, 0, 0 }, { 55, 63, 0 } } },
- { { { 59, 0, 1 }, { 56, 63, 1 } } },
- { { { 59, 0, 2 }, { 56, 63, 0 } } },
- { { { 60, 0, 1 }, { 57, 63, 1 } } },
- { { { 60, 0, 0 }, { 57, 63, 0 } } },
- { { { 60, 0, 1 }, { 58, 63, 1 } } },
- { { { 60, 0, 2 }, { 58, 63, 0 } } },
- { { { 61, 0, 1 }, { 59, 63, 1 } } },
- { { { 61, 0, 0 }, { 59, 63, 0 } } },
- { { { 61, 0, 1 }, { 60, 63, 1 } } },
- { { { 61, 0, 2 }, { 60, 63, 0 } } },
- { { { 62, 0, 1 }, { 61, 63, 1 } } },
- { { { 62, 0, 0 }, { 61, 63, 0 } } },
- { { { 62, 0, 1 }, { 62, 63, 1 } } },
- { { { 62, 0, 2 }, { 62, 63, 0 } } },
- { { { 63, 0, 1 }, { 63, 63, 1 } } },
- { { { 63, 0, 0 }, { 63, 63, 0 } } }
-};
-
-static SingleColourLookup const lookup_5_4[] =
-{
- { { { 0, 0, 0 }, { 0, 0, 0 } } },
- { { { 0, 0, 1 }, { 0, 1, 1 } } },
- { { { 0, 0, 2 }, { 0, 1, 0 } } },
- { { { 0, 0, 3 }, { 0, 1, 1 } } },
- { { { 0, 0, 4 }, { 0, 2, 1 } } },
- { { { 1, 0, 3 }, { 0, 2, 0 } } },
- { { { 1, 0, 2 }, { 0, 2, 1 } } },
- { { { 1, 0, 1 }, { 0, 3, 1 } } },
- { { { 1, 0, 0 }, { 0, 3, 0 } } },
- { { { 1, 0, 1 }, { 1, 2, 1 } } },
- { { { 1, 0, 2 }, { 1, 2, 0 } } },
- { { { 1, 0, 3 }, { 0, 4, 0 } } },
- { { { 1, 0, 4 }, { 0, 5, 1 } } },
- { { { 2, 0, 3 }, { 0, 5, 0 } } },
- { { { 2, 0, 2 }, { 0, 5, 1 } } },
- { { { 2, 0, 1 }, { 0, 6, 1 } } },
- { { { 2, 0, 0 }, { 0, 6, 0 } } },
- { { { 2, 0, 1 }, { 2, 3, 1 } } },
- { { { 2, 0, 2 }, { 2, 3, 0 } } },
- { { { 2, 0, 3 }, { 0, 7, 0 } } },
- { { { 2, 0, 4 }, { 1, 6, 1 } } },
- { { { 3, 0, 3 }, { 1, 6, 0 } } },
- { { { 3, 0, 2 }, { 0, 8, 0 } } },
- { { { 3, 0, 1 }, { 0, 9, 1 } } },
- { { { 3, 0, 0 }, { 0, 9, 0 } } },
- { { { 3, 0, 1 }, { 0, 9, 1 } } },
- { { { 3, 0, 2 }, { 0, 10, 1 } } },
- { { { 3, 0, 3 }, { 0, 10, 0 } } },
- { { { 3, 0, 4 }, { 2, 7, 1 } } },
- { { { 4, 0, 4 }, { 2, 7, 0 } } },
- { { { 4, 0, 3 }, { 0, 11, 0 } } },
- { { { 4, 0, 2 }, { 1, 10, 1 } } },
- { { { 4, 0, 1 }, { 1, 10, 0 } } },
- { { { 4, 0, 0 }, { 0, 12, 0 } } },
- { { { 4, 0, 1 }, { 0, 13, 1 } } },
- { { { 4, 0, 2 }, { 0, 13, 0 } } },
- { { { 4, 0, 3 }, { 0, 13, 1 } } },
- { { { 4, 0, 4 }, { 0, 14, 1 } } },
- { { { 5, 0, 3 }, { 0, 14, 0 } } },
- { { { 5, 0, 2 }, { 2, 11, 1 } } },
- { { { 5, 0, 1 }, { 2, 11, 0 } } },
- { { { 5, 0, 0 }, { 0, 15, 0 } } },
- { { { 5, 0, 1 }, { 1, 14, 1 } } },
- { { { 5, 0, 2 }, { 1, 14, 0 } } },
- { { { 5, 0, 3 }, { 0, 16, 0 } } },
- { { { 5, 0, 4 }, { 0, 17, 1 } } },
- { { { 6, 0, 3 }, { 0, 17, 0 } } },
- { { { 6, 0, 2 }, { 0, 17, 1 } } },
- { { { 6, 0, 1 }, { 0, 18, 1 } } },
- { { { 6, 0, 0 }, { 0, 18, 0 } } },
- { { { 6, 0, 1 }, { 2, 15, 1 } } },
- { { { 6, 0, 2 }, { 2, 15, 0 } } },
- { { { 6, 0, 3 }, { 0, 19, 0 } } },
- { { { 6, 0, 4 }, { 1, 18, 1 } } },
- { { { 7, 0, 3 }, { 1, 18, 0 } } },
- { { { 7, 0, 2 }, { 0, 20, 0 } } },
- { { { 7, 0, 1 }, { 0, 21, 1 } } },
- { { { 7, 0, 0 }, { 0, 21, 0 } } },
- { { { 7, 0, 1 }, { 0, 21, 1 } } },
- { { { 7, 0, 2 }, { 0, 22, 1 } } },
- { { { 7, 0, 3 }, { 0, 22, 0 } } },
- { { { 7, 0, 4 }, { 2, 19, 1 } } },
- { { { 8, 0, 4 }, { 2, 19, 0 } } },
- { { { 8, 0, 3 }, { 0, 23, 0 } } },
- { { { 8, 0, 2 }, { 1, 22, 1 } } },
- { { { 8, 0, 1 }, { 1, 22, 0 } } },
- { { { 8, 0, 0 }, { 0, 24, 0 } } },
- { { { 8, 0, 1 }, { 0, 25, 1 } } },
- { { { 8, 0, 2 }, { 0, 25, 0 } } },
- { { { 8, 0, 3 }, { 0, 25, 1 } } },
- { { { 8, 0, 4 }, { 0, 26, 1 } } },
- { { { 9, 0, 3 }, { 0, 26, 0 } } },
- { { { 9, 0, 2 }, { 2, 23, 1 } } },
- { { { 9, 0, 1 }, { 2, 23, 0 } } },
- { { { 9, 0, 0 }, { 0, 27, 0 } } },
- { { { 9, 0, 1 }, { 1, 26, 1 } } },
- { { { 9, 0, 2 }, { 1, 26, 0 } } },
- { { { 9, 0, 3 }, { 0, 28, 0 } } },
- { { { 9, 0, 4 }, { 0, 29, 1 } } },
- { { { 10, 0, 3 }, { 0, 29, 0 } } },
- { { { 10, 0, 2 }, { 0, 29, 1 } } },
- { { { 10, 0, 1 }, { 0, 30, 1 } } },
- { { { 10, 0, 0 }, { 0, 30, 0 } } },
- { { { 10, 0, 1 }, { 2, 27, 1 } } },
- { { { 10, 0, 2 }, { 2, 27, 0 } } },
- { { { 10, 0, 3 }, { 0, 31, 0 } } },
- { { { 10, 0, 4 }, { 1, 30, 1 } } },
- { { { 11, 0, 3 }, { 1, 30, 0 } } },
- { { { 11, 0, 2 }, { 4, 24, 0 } } },
- { { { 11, 0, 1 }, { 1, 31, 1 } } },
- { { { 11, 0, 0 }, { 1, 31, 0 } } },
- { { { 11, 0, 1 }, { 1, 31, 1 } } },
- { { { 11, 0, 2 }, { 2, 30, 1 } } },
- { { { 11, 0, 3 }, { 2, 30, 0 } } },
- { { { 11, 0, 4 }, { 2, 31, 1 } } },
- { { { 12, 0, 4 }, { 2, 31, 0 } } },
- { { { 12, 0, 3 }, { 4, 27, 0 } } },
- { { { 12, 0, 2 }, { 3, 30, 1 } } },
- { { { 12, 0, 1 }, { 3, 30, 0 } } },
- { { { 12, 0, 0 }, { 4, 28, 0 } } },
- { { { 12, 0, 1 }, { 3, 31, 1 } } },
- { { { 12, 0, 2 }, { 3, 31, 0 } } },
- { { { 12, 0, 3 }, { 3, 31, 1 } } },
- { { { 12, 0, 4 }, { 4, 30, 1 } } },
- { { { 13, 0, 3 }, { 4, 30, 0 } } },
- { { { 13, 0, 2 }, { 6, 27, 1 } } },
- { { { 13, 0, 1 }, { 6, 27, 0 } } },
- { { { 13, 0, 0 }, { 4, 31, 0 } } },
- { { { 13, 0, 1 }, { 5, 30, 1 } } },
- { { { 13, 0, 2 }, { 5, 30, 0 } } },
- { { { 13, 0, 3 }, { 8, 24, 0 } } },
- { { { 13, 0, 4 }, { 5, 31, 1 } } },
- { { { 14, 0, 3 }, { 5, 31, 0 } } },
- { { { 14, 0, 2 }, { 5, 31, 1 } } },
- { { { 14, 0, 1 }, { 6, 30, 1 } } },
- { { { 14, 0, 0 }, { 6, 30, 0 } } },
- { { { 14, 0, 1 }, { 6, 31, 1 } } },
- { { { 14, 0, 2 }, { 6, 31, 0 } } },
- { { { 14, 0, 3 }, { 8, 27, 0 } } },
- { { { 14, 0, 4 }, { 7, 30, 1 } } },
- { { { 15, 0, 3 }, { 7, 30, 0 } } },
- { { { 15, 0, 2 }, { 8, 28, 0 } } },
- { { { 15, 0, 1 }, { 7, 31, 1 } } },
- { { { 15, 0, 0 }, { 7, 31, 0 } } },
- { { { 15, 0, 1 }, { 7, 31, 1 } } },
- { { { 15, 0, 2 }, { 8, 30, 1 } } },
- { { { 15, 0, 3 }, { 8, 30, 0 } } },
- { { { 15, 0, 4 }, { 10, 27, 1 } } },
- { { { 16, 0, 4 }, { 10, 27, 0 } } },
- { { { 16, 0, 3 }, { 8, 31, 0 } } },
- { { { 16, 0, 2 }, { 9, 30, 1 } } },
- { { { 16, 0, 1 }, { 9, 30, 0 } } },
- { { { 16, 0, 0 }, { 12, 24, 0 } } },
- { { { 16, 0, 1 }, { 9, 31, 1 } } },
- { { { 16, 0, 2 }, { 9, 31, 0 } } },
- { { { 16, 0, 3 }, { 9, 31, 1 } } },
- { { { 16, 0, 4 }, { 10, 30, 1 } } },
- { { { 17, 0, 3 }, { 10, 30, 0 } } },
- { { { 17, 0, 2 }, { 10, 31, 1 } } },
- { { { 17, 0, 1 }, { 10, 31, 0 } } },
- { { { 17, 0, 0 }, { 12, 27, 0 } } },
- { { { 17, 0, 1 }, { 11, 30, 1 } } },
- { { { 17, 0, 2 }, { 11, 30, 0 } } },
- { { { 17, 0, 3 }, { 12, 28, 0 } } },
- { { { 17, 0, 4 }, { 11, 31, 1 } } },
- { { { 18, 0, 3 }, { 11, 31, 0 } } },
- { { { 18, 0, 2 }, { 11, 31, 1 } } },
- { { { 18, 0, 1 }, { 12, 30, 1 } } },
- { { { 18, 0, 0 }, { 12, 30, 0 } } },
- { { { 18, 0, 1 }, { 14, 27, 1 } } },
- { { { 18, 0, 2 }, { 14, 27, 0 } } },
- { { { 18, 0, 3 }, { 12, 31, 0 } } },
- { { { 18, 0, 4 }, { 13, 30, 1 } } },
- { { { 19, 0, 3 }, { 13, 30, 0 } } },
- { { { 19, 0, 2 }, { 16, 24, 0 } } },
- { { { 19, 0, 1 }, { 13, 31, 1 } } },
- { { { 19, 0, 0 }, { 13, 31, 0 } } },
- { { { 19, 0, 1 }, { 13, 31, 1 } } },
- { { { 19, 0, 2 }, { 14, 30, 1 } } },
- { { { 19, 0, 3 }, { 14, 30, 0 } } },
- { { { 19, 0, 4 }, { 14, 31, 1 } } },
- { { { 20, 0, 4 }, { 14, 31, 0 } } },
- { { { 20, 0, 3 }, { 16, 27, 0 } } },
- { { { 20, 0, 2 }, { 15, 30, 1 } } },
- { { { 20, 0, 1 }, { 15, 30, 0 } } },
- { { { 20, 0, 0 }, { 16, 28, 0 } } },
- { { { 20, 0, 1 }, { 15, 31, 1 } } },
- { { { 20, 0, 2 }, { 15, 31, 0 } } },
- { { { 20, 0, 3 }, { 15, 31, 1 } } },
- { { { 20, 0, 4 }, { 16, 30, 1 } } },
- { { { 21, 0, 3 }, { 16, 30, 0 } } },
- { { { 21, 0, 2 }, { 18, 27, 1 } } },
- { { { 21, 0, 1 }, { 18, 27, 0 } } },
- { { { 21, 0, 0 }, { 16, 31, 0 } } },
- { { { 21, 0, 1 }, { 17, 30, 1 } } },
- { { { 21, 0, 2 }, { 17, 30, 0 } } },
- { { { 21, 0, 3 }, { 20, 24, 0 } } },
- { { { 21, 0, 4 }, { 17, 31, 1 } } },
- { { { 22, 0, 3 }, { 17, 31, 0 } } },
- { { { 22, 0, 2 }, { 17, 31, 1 } } },
- { { { 22, 0, 1 }, { 18, 30, 1 } } },
- { { { 22, 0, 0 }, { 18, 30, 0 } } },
- { { { 22, 0, 1 }, { 18, 31, 1 } } },
- { { { 22, 0, 2 }, { 18, 31, 0 } } },
- { { { 22, 0, 3 }, { 20, 27, 0 } } },
- { { { 22, 0, 4 }, { 19, 30, 1 } } },
- { { { 23, 0, 3 }, { 19, 30, 0 } } },
- { { { 23, 0, 2 }, { 20, 28, 0 } } },
- { { { 23, 0, 1 }, { 19, 31, 1 } } },
- { { { 23, 0, 0 }, { 19, 31, 0 } } },
- { { { 23, 0, 1 }, { 19, 31, 1 } } },
- { { { 23, 0, 2 }, { 20, 30, 1 } } },
- { { { 23, 0, 3 }, { 20, 30, 0 } } },
- { { { 23, 0, 4 }, { 22, 27, 1 } } },
- { { { 24, 0, 4 }, { 22, 27, 0 } } },
- { { { 24, 0, 3 }, { 20, 31, 0 } } },
- { { { 24, 0, 2 }, { 21, 30, 1 } } },
- { { { 24, 0, 1 }, { 21, 30, 0 } } },
- { { { 24, 0, 0 }, { 24, 24, 0 } } },
- { { { 24, 0, 1 }, { 21, 31, 1 } } },
- { { { 24, 0, 2 }, { 21, 31, 0 } } },
- { { { 24, 0, 3 }, { 21, 31, 1 } } },
- { { { 24, 0, 4 }, { 22, 30, 1 } } },
- { { { 25, 0, 3 }, { 22, 30, 0 } } },
- { { { 25, 0, 2 }, { 22, 31, 1 } } },
- { { { 25, 0, 1 }, { 22, 31, 0 } } },
- { { { 25, 0, 0 }, { 24, 27, 0 } } },
- { { { 25, 0, 1 }, { 23, 30, 1 } } },
- { { { 25, 0, 2 }, { 23, 30, 0 } } },
- { { { 25, 0, 3 }, { 24, 28, 0 } } },
- { { { 25, 0, 4 }, { 23, 31, 1 } } },
- { { { 26, 0, 3 }, { 23, 31, 0 } } },
- { { { 26, 0, 2 }, { 23, 31, 1 } } },
- { { { 26, 0, 1 }, { 24, 30, 1 } } },
- { { { 26, 0, 0 }, { 24, 30, 0 } } },
- { { { 26, 0, 1 }, { 26, 27, 1 } } },
- { { { 26, 0, 2 }, { 26, 27, 0 } } },
- { { { 26, 0, 3 }, { 24, 31, 0 } } },
- { { { 26, 0, 4 }, { 25, 30, 1 } } },
- { { { 27, 0, 3 }, { 25, 30, 0 } } },
- { { { 27, 0, 2 }, { 28, 24, 0 } } },
- { { { 27, 0, 1 }, { 25, 31, 1 } } },
- { { { 27, 0, 0 }, { 25, 31, 0 } } },
- { { { 27, 0, 1 }, { 25, 31, 1 } } },
- { { { 27, 0, 2 }, { 26, 30, 1 } } },
- { { { 27, 0, 3 }, { 26, 30, 0 } } },
- { { { 27, 0, 4 }, { 26, 31, 1 } } },
- { { { 28, 0, 4 }, { 26, 31, 0 } } },
- { { { 28, 0, 3 }, { 28, 27, 0 } } },
- { { { 28, 0, 2 }, { 27, 30, 1 } } },
- { { { 28, 0, 1 }, { 27, 30, 0 } } },
- { { { 28, 0, 0 }, { 28, 28, 0 } } },
- { { { 28, 0, 1 }, { 27, 31, 1 } } },
- { { { 28, 0, 2 }, { 27, 31, 0 } } },
- { { { 28, 0, 3 }, { 27, 31, 1 } } },
- { { { 28, 0, 4 }, { 28, 30, 1 } } },
- { { { 29, 0, 3 }, { 28, 30, 0 } } },
- { { { 29, 0, 2 }, { 30, 27, 1 } } },
- { { { 29, 0, 1 }, { 30, 27, 0 } } },
- { { { 29, 0, 0 }, { 28, 31, 0 } } },
- { { { 29, 0, 1 }, { 29, 30, 1 } } },
- { { { 29, 0, 2 }, { 29, 30, 0 } } },
- { { { 29, 0, 3 }, { 29, 30, 1 } } },
- { { { 29, 0, 4 }, { 29, 31, 1 } } },
- { { { 30, 0, 3 }, { 29, 31, 0 } } },
- { { { 30, 0, 2 }, { 29, 31, 1 } } },
- { { { 30, 0, 1 }, { 30, 30, 1 } } },
- { { { 30, 0, 0 }, { 30, 30, 0 } } },
- { { { 30, 0, 1 }, { 30, 31, 1 } } },
- { { { 30, 0, 2 }, { 30, 31, 0 } } },
- { { { 30, 0, 3 }, { 30, 31, 1 } } },
- { { { 30, 0, 4 }, { 31, 30, 1 } } },
- { { { 31, 0, 3 }, { 31, 30, 0 } } },
- { { { 31, 0, 2 }, { 31, 30, 1 } } },
- { { { 31, 0, 1 }, { 31, 31, 1 } } },
- { { { 31, 0, 0 }, { 31, 31, 0 } } }
-};
-
-static SingleColourLookup const lookup_6_4[] =
-{
- { { { 0, 0, 0 }, { 0, 0, 0 } } },
- { { { 0, 0, 1 }, { 0, 1, 0 } } },
- { { { 0, 0, 2 }, { 0, 2, 0 } } },
- { { { 1, 0, 1 }, { 0, 3, 1 } } },
- { { { 1, 0, 0 }, { 0, 3, 0 } } },
- { { { 1, 0, 1 }, { 0, 4, 0 } } },
- { { { 1, 0, 2 }, { 0, 5, 0 } } },
- { { { 2, 0, 1 }, { 0, 6, 1 } } },
- { { { 2, 0, 0 }, { 0, 6, 0 } } },
- { { { 2, 0, 1 }, { 0, 7, 0 } } },
- { { { 2, 0, 2 }, { 0, 8, 0 } } },
- { { { 3, 0, 1 }, { 0, 9, 1 } } },
- { { { 3, 0, 0 }, { 0, 9, 0 } } },
- { { { 3, 0, 1 }, { 0, 10, 0 } } },
- { { { 3, 0, 2 }, { 0, 11, 0 } } },
- { { { 4, 0, 1 }, { 0, 12, 1 } } },
- { { { 4, 0, 0 }, { 0, 12, 0 } } },
- { { { 4, 0, 1 }, { 0, 13, 0 } } },
- { { { 4, 0, 2 }, { 0, 14, 0 } } },
- { { { 5, 0, 1 }, { 0, 15, 1 } } },
- { { { 5, 0, 0 }, { 0, 15, 0 } } },
- { { { 5, 0, 1 }, { 0, 16, 0 } } },
- { { { 5, 0, 2 }, { 1, 15, 0 } } },
- { { { 6, 0, 1 }, { 0, 17, 0 } } },
- { { { 6, 0, 0 }, { 0, 18, 0 } } },
- { { { 6, 0, 1 }, { 0, 19, 0 } } },
- { { { 6, 0, 2 }, { 3, 14, 0 } } },
- { { { 7, 0, 1 }, { 0, 20, 0 } } },
- { { { 7, 0, 0 }, { 0, 21, 0 } } },
- { { { 7, 0, 1 }, { 0, 22, 0 } } },
- { { { 7, 0, 2 }, { 4, 15, 0 } } },
- { { { 8, 0, 1 }, { 0, 23, 0 } } },
- { { { 8, 0, 0 }, { 0, 24, 0 } } },
- { { { 8, 0, 1 }, { 0, 25, 0 } } },
- { { { 8, 0, 2 }, { 6, 14, 0 } } },
- { { { 9, 0, 1 }, { 0, 26, 0 } } },
- { { { 9, 0, 0 }, { 0, 27, 0 } } },
- { { { 9, 0, 1 }, { 0, 28, 0 } } },
- { { { 9, 0, 2 }, { 7, 15, 0 } } },
- { { { 10, 0, 1 }, { 0, 29, 0 } } },
- { { { 10, 0, 0 }, { 0, 30, 0 } } },
- { { { 10, 0, 1 }, { 0, 31, 0 } } },
- { { { 10, 0, 2 }, { 9, 14, 0 } } },
- { { { 11, 0, 1 }, { 0, 32, 0 } } },
- { { { 11, 0, 0 }, { 0, 33, 0 } } },
- { { { 11, 0, 1 }, { 2, 30, 0 } } },
- { { { 11, 0, 2 }, { 0, 34, 0 } } },
- { { { 12, 0, 1 }, { 0, 35, 0 } } },
- { { { 12, 0, 0 }, { 0, 36, 0 } } },
- { { { 12, 0, 1 }, { 3, 31, 0 } } },
- { { { 12, 0, 2 }, { 0, 37, 0 } } },
- { { { 13, 0, 1 }, { 0, 38, 0 } } },
- { { { 13, 0, 0 }, { 0, 39, 0 } } },
- { { { 13, 0, 1 }, { 5, 30, 0 } } },
- { { { 13, 0, 2 }, { 0, 40, 0 } } },
- { { { 14, 0, 1 }, { 0, 41, 0 } } },
- { { { 14, 0, 0 }, { 0, 42, 0 } } },
- { { { 14, 0, 1 }, { 6, 31, 0 } } },
- { { { 14, 0, 2 }, { 0, 43, 0 } } },
- { { { 15, 0, 1 }, { 0, 44, 0 } } },
- { { { 15, 0, 0 }, { 0, 45, 0 } } },
- { { { 15, 0, 1 }, { 8, 30, 0 } } },
- { { { 15, 0, 2 }, { 0, 46, 0 } } },
- { { { 16, 0, 2 }, { 0, 47, 0 } } },
- { { { 16, 0, 1 }, { 1, 46, 0 } } },
- { { { 16, 0, 0 }, { 0, 48, 0 } } },
- { { { 16, 0, 1 }, { 0, 49, 0 } } },
- { { { 16, 0, 2 }, { 0, 50, 0 } } },
- { { { 17, 0, 1 }, { 2, 47, 0 } } },
- { { { 17, 0, 0 }, { 0, 51, 0 } } },
- { { { 17, 0, 1 }, { 0, 52, 0 } } },
- { { { 17, 0, 2 }, { 0, 53, 0 } } },
- { { { 18, 0, 1 }, { 4, 46, 0 } } },
- { { { 18, 0, 0 }, { 0, 54, 0 } } },
- { { { 18, 0, 1 }, { 0, 55, 0 } } },
- { { { 18, 0, 2 }, { 0, 56, 0 } } },
- { { { 19, 0, 1 }, { 5, 47, 0 } } },
- { { { 19, 0, 0 }, { 0, 57, 0 } } },
- { { { 19, 0, 1 }, { 0, 58, 0 } } },
- { { { 19, 0, 2 }, { 0, 59, 0 } } },
- { { { 20, 0, 1 }, { 7, 46, 0 } } },
- { { { 20, 0, 0 }, { 0, 60, 0 } } },
- { { { 20, 0, 1 }, { 0, 61, 0 } } },
- { { { 20, 0, 2 }, { 0, 62, 0 } } },
- { { { 21, 0, 1 }, { 8, 47, 0 } } },
- { { { 21, 0, 0 }, { 0, 63, 0 } } },
- { { { 21, 0, 1 }, { 1, 62, 0 } } },
- { { { 21, 0, 2 }, { 1, 63, 0 } } },
- { { { 22, 0, 1 }, { 10, 46, 0 } } },
- { { { 22, 0, 0 }, { 2, 62, 0 } } },
- { { { 22, 0, 1 }, { 2, 63, 0 } } },
- { { { 22, 0, 2 }, { 3, 62, 0 } } },
- { { { 23, 0, 1 }, { 11, 47, 0 } } },
- { { { 23, 0, 0 }, { 3, 63, 0 } } },
- { { { 23, 0, 1 }, { 4, 62, 0 } } },
- { { { 23, 0, 2 }, { 4, 63, 0 } } },
- { { { 24, 0, 1 }, { 13, 46, 0 } } },
- { { { 24, 0, 0 }, { 5, 62, 0 } } },
- { { { 24, 0, 1 }, { 5, 63, 0 } } },
- { { { 24, 0, 2 }, { 6, 62, 0 } } },
- { { { 25, 0, 1 }, { 14, 47, 0 } } },
- { { { 25, 0, 0 }, { 6, 63, 0 } } },
- { { { 25, 0, 1 }, { 7, 62, 0 } } },
- { { { 25, 0, 2 }, { 7, 63, 0 } } },
- { { { 26, 0, 1 }, { 16, 45, 0 } } },
- { { { 26, 0, 0 }, { 8, 62, 0 } } },
- { { { 26, 0, 1 }, { 8, 63, 0 } } },
- { { { 26, 0, 2 }, { 9, 62, 0 } } },
- { { { 27, 0, 1 }, { 16, 48, 0 } } },
- { { { 27, 0, 0 }, { 9, 63, 0 } } },
- { { { 27, 0, 1 }, { 10, 62, 0 } } },
- { { { 27, 0, 2 }, { 10, 63, 0 } } },
- { { { 28, 0, 1 }, { 16, 51, 0 } } },
- { { { 28, 0, 0 }, { 11, 62, 0 } } },
- { { { 28, 0, 1 }, { 11, 63, 0 } } },
- { { { 28, 0, 2 }, { 12, 62, 0 } } },
- { { { 29, 0, 1 }, { 16, 54, 0 } } },
- { { { 29, 0, 0 }, { 12, 63, 0 } } },
- { { { 29, 0, 1 }, { 13, 62, 0 } } },
- { { { 29, 0, 2 }, { 13, 63, 0 } } },
- { { { 30, 0, 1 }, { 16, 57, 0 } } },
- { { { 30, 0, 0 }, { 14, 62, 0 } } },
- { { { 30, 0, 1 }, { 14, 63, 0 } } },
- { { { 30, 0, 2 }, { 15, 62, 0 } } },
- { { { 31, 0, 1 }, { 16, 60, 0 } } },
- { { { 31, 0, 0 }, { 15, 63, 0 } } },
- { { { 31, 0, 1 }, { 24, 46, 0 } } },
- { { { 31, 0, 2 }, { 16, 62, 0 } } },
- { { { 32, 0, 2 }, { 16, 63, 0 } } },
- { { { 32, 0, 1 }, { 17, 62, 0 } } },
- { { { 32, 0, 0 }, { 25, 47, 0 } } },
- { { { 32, 0, 1 }, { 17, 63, 0 } } },
- { { { 32, 0, 2 }, { 18, 62, 0 } } },
- { { { 33, 0, 1 }, { 18, 63, 0 } } },
- { { { 33, 0, 0 }, { 27, 46, 0 } } },
- { { { 33, 0, 1 }, { 19, 62, 0 } } },
- { { { 33, 0, 2 }, { 19, 63, 0 } } },
- { { { 34, 0, 1 }, { 20, 62, 0 } } },
- { { { 34, 0, 0 }, { 28, 47, 0 } } },
- { { { 34, 0, 1 }, { 20, 63, 0 } } },
- { { { 34, 0, 2 }, { 21, 62, 0 } } },
- { { { 35, 0, 1 }, { 21, 63, 0 } } },
- { { { 35, 0, 0 }, { 30, 46, 0 } } },
- { { { 35, 0, 1 }, { 22, 62, 0 } } },
- { { { 35, 0, 2 }, { 22, 63, 0 } } },
- { { { 36, 0, 1 }, { 23, 62, 0 } } },
- { { { 36, 0, 0 }, { 31, 47, 0 } } },
- { { { 36, 0, 1 }, { 23, 63, 0 } } },
- { { { 36, 0, 2 }, { 24, 62, 0 } } },
- { { { 37, 0, 1 }, { 24, 63, 0 } } },
- { { { 37, 0, 0 }, { 32, 47, 0 } } },
- { { { 37, 0, 1 }, { 25, 62, 0 } } },
- { { { 37, 0, 2 }, { 25, 63, 0 } } },
- { { { 38, 0, 1 }, { 26, 62, 0 } } },
- { { { 38, 0, 0 }, { 32, 50, 0 } } },
- { { { 38, 0, 1 }, { 26, 63, 0 } } },
- { { { 38, 0, 2 }, { 27, 62, 0 } } },
- { { { 39, 0, 1 }, { 27, 63, 0 } } },
- { { { 39, 0, 0 }, { 32, 53, 0 } } },
- { { { 39, 0, 1 }, { 28, 62, 0 } } },
- { { { 39, 0, 2 }, { 28, 63, 0 } } },
- { { { 40, 0, 1 }, { 29, 62, 0 } } },
- { { { 40, 0, 0 }, { 32, 56, 0 } } },
- { { { 40, 0, 1 }, { 29, 63, 0 } } },
- { { { 40, 0, 2 }, { 30, 62, 0 } } },
- { { { 41, 0, 1 }, { 30, 63, 0 } } },
- { { { 41, 0, 0 }, { 32, 59, 0 } } },
- { { { 41, 0, 1 }, { 31, 62, 0 } } },
- { { { 41, 0, 2 }, { 31, 63, 0 } } },
- { { { 42, 0, 1 }, { 32, 61, 0 } } },
- { { { 42, 0, 0 }, { 32, 62, 0 } } },
- { { { 42, 0, 1 }, { 32, 63, 0 } } },
- { { { 42, 0, 2 }, { 41, 46, 0 } } },
- { { { 43, 0, 1 }, { 33, 62, 0 } } },
- { { { 43, 0, 0 }, { 33, 63, 0 } } },
- { { { 43, 0, 1 }, { 34, 62, 0 } } },
- { { { 43, 0, 2 }, { 42, 47, 0 } } },
- { { { 44, 0, 1 }, { 34, 63, 0 } } },
- { { { 44, 0, 0 }, { 35, 62, 0 } } },
- { { { 44, 0, 1 }, { 35, 63, 0 } } },
- { { { 44, 0, 2 }, { 44, 46, 0 } } },
- { { { 45, 0, 1 }, { 36, 62, 0 } } },
- { { { 45, 0, 0 }, { 36, 63, 0 } } },
- { { { 45, 0, 1 }, { 37, 62, 0 } } },
- { { { 45, 0, 2 }, { 45, 47, 0 } } },
- { { { 46, 0, 1 }, { 37, 63, 0 } } },
- { { { 46, 0, 0 }, { 38, 62, 0 } } },
- { { { 46, 0, 1 }, { 38, 63, 0 } } },
- { { { 46, 0, 2 }, { 47, 46, 0 } } },
- { { { 47, 0, 1 }, { 39, 62, 0 } } },
- { { { 47, 0, 0 }, { 39, 63, 0 } } },
- { { { 47, 0, 1 }, { 40, 62, 0 } } },
- { { { 47, 0, 2 }, { 48, 46, 0 } } },
- { { { 48, 0, 2 }, { 40, 63, 0 } } },
- { { { 48, 0, 1 }, { 41, 62, 0 } } },
- { { { 48, 0, 0 }, { 41, 63, 0 } } },
- { { { 48, 0, 1 }, { 48, 49, 0 } } },
- { { { 48, 0, 2 }, { 42, 62, 0 } } },
- { { { 49, 0, 1 }, { 42, 63, 0 } } },
- { { { 49, 0, 0 }, { 43, 62, 0 } } },
- { { { 49, 0, 1 }, { 48, 52, 0 } } },
- { { { 49, 0, 2 }, { 43, 63, 0 } } },
- { { { 50, 0, 1 }, { 44, 62, 0 } } },
- { { { 50, 0, 0 }, { 44, 63, 0 } } },
- { { { 50, 0, 1 }, { 48, 55, 0 } } },
- { { { 50, 0, 2 }, { 45, 62, 0 } } },
- { { { 51, 0, 1 }, { 45, 63, 0 } } },
- { { { 51, 0, 0 }, { 46, 62, 0 } } },
- { { { 51, 0, 1 }, { 48, 58, 0 } } },
- { { { 51, 0, 2 }, { 46, 63, 0 } } },
- { { { 52, 0, 1 }, { 47, 62, 0 } } },
- { { { 52, 0, 0 }, { 47, 63, 0 } } },
- { { { 52, 0, 1 }, { 48, 61, 0 } } },
- { { { 52, 0, 2 }, { 48, 62, 0 } } },
- { { { 53, 0, 1 }, { 56, 47, 0 } } },
- { { { 53, 0, 0 }, { 48, 63, 0 } } },
- { { { 53, 0, 1 }, { 49, 62, 0 } } },
- { { { 53, 0, 2 }, { 49, 63, 0 } } },
- { { { 54, 0, 1 }, { 58, 46, 0 } } },
- { { { 54, 0, 0 }, { 50, 62, 0 } } },
- { { { 54, 0, 1 }, { 50, 63, 0 } } },
- { { { 54, 0, 2 }, { 51, 62, 0 } } },
- { { { 55, 0, 1 }, { 59, 47, 0 } } },
- { { { 55, 0, 0 }, { 51, 63, 0 } } },
- { { { 55, 0, 1 }, { 52, 62, 0 } } },
- { { { 55, 0, 2 }, { 52, 63, 0 } } },
- { { { 56, 0, 1 }, { 61, 46, 0 } } },
- { { { 56, 0, 0 }, { 53, 62, 0 } } },
- { { { 56, 0, 1 }, { 53, 63, 0 } } },
- { { { 56, 0, 2 }, { 54, 62, 0 } } },
- { { { 57, 0, 1 }, { 62, 47, 0 } } },
- { { { 57, 0, 0 }, { 54, 63, 0 } } },
- { { { 57, 0, 1 }, { 55, 62, 0 } } },
- { { { 57, 0, 2 }, { 55, 63, 0 } } },
- { { { 58, 0, 1 }, { 56, 62, 1 } } },
- { { { 58, 0, 0 }, { 56, 62, 0 } } },
- { { { 58, 0, 1 }, { 56, 63, 0 } } },
- { { { 58, 0, 2 }, { 57, 62, 0 } } },
- { { { 59, 0, 1 }, { 57, 63, 1 } } },
- { { { 59, 0, 0 }, { 57, 63, 0 } } },
- { { { 59, 0, 1 }, { 58, 62, 0 } } },
- { { { 59, 0, 2 }, { 58, 63, 0 } } },
- { { { 60, 0, 1 }, { 59, 62, 1 } } },
- { { { 60, 0, 0 }, { 59, 62, 0 } } },
- { { { 60, 0, 1 }, { 59, 63, 0 } } },
- { { { 60, 0, 2 }, { 60, 62, 0 } } },
- { { { 61, 0, 1 }, { 60, 63, 1 } } },
- { { { 61, 0, 0 }, { 60, 63, 0 } } },
- { { { 61, 0, 1 }, { 61, 62, 0 } } },
- { { { 61, 0, 2 }, { 61, 63, 0 } } },
- { { { 62, 0, 1 }, { 62, 62, 1 } } },
- { { { 62, 0, 0 }, { 62, 62, 0 } } },
- { { { 62, 0, 1 }, { 62, 63, 0 } } },
- { { { 62, 0, 2 }, { 63, 62, 0 } } },
- { { { 63, 0, 1 }, { 63, 63, 1 } } },
- { { { 63, 0, 0 }, { 63, 63, 0 } } }
-};
diff --git a/thirdparty/squish/squish.cpp b/thirdparty/squish/squish.cpp
deleted file mode 100644
index 1de1da3e52..0000000000
--- a/thirdparty/squish/squish.cpp
+++ /dev/null
@@ -1,411 +0,0 @@
-/* -----------------------------------------------------------------------------
-
- Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
-
- 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 <string.h>
-#include "squish.h"
-#include "colourset.h"
-#include "maths.h"
-#include "rangefit.h"
-#include "clusterfit.h"
-#include "colourblock.h"
-#include "alpha.h"
-#include "singlecolourfit.h"
-
-namespace squish {
-
-static int FixFlags( int flags )
-{
- // grab the flag bits
- int method = flags & ( kDxt1 | kDxt3 | kDxt5 | kBc4 | kBc5 );
- int fit = flags & ( kColourIterativeClusterFit | kColourClusterFit | kColourRangeFit );
- int extra = flags & kWeightColourByAlpha;
-
- // set defaults
- if ( method != kDxt3
- && method != kDxt5
- && method != kBc4
- && method != kBc5 )
- {
- method = kDxt1;
- }
- if( fit != kColourRangeFit && fit != kColourIterativeClusterFit )
- fit = kColourClusterFit;
-
- // done
- return method | fit | extra;
-}
-
-void CompressMasked( u8 const* rgba, int mask, void* block, int flags, float* metric )
-{
- // fix any bad flags
- flags = FixFlags( flags );
-
- if ( ( flags & ( kBc4 | kBc5 ) ) != 0 )
- {
- u8 alpha[16*4];
- for( int i = 0; i < 16; ++i )
- {
- alpha[i*4 + 3] = rgba[i*4 + 0]; // copy R to A
- }
-
- u8* rBlock = reinterpret_cast< u8* >( block );
- CompressAlphaDxt5( alpha, mask, rBlock );
-
- if ( ( flags & ( kBc5 ) ) != 0 )
- {
- for( int i = 0; i < 16; ++i )
- {
- alpha[i*4 + 3] = rgba[i*4 + 1]; // copy G to A
- }
-
- u8* gBlock = reinterpret_cast< u8* >( block ) + 8;
- CompressAlphaDxt5( alpha, mask, gBlock );
- }
-
- return;
- }
-
- // get the block locations
- void* colourBlock = block;
- void* alphaBlock = block;
- if( ( flags & ( kDxt3 | kDxt5 ) ) != 0 )
- colourBlock = reinterpret_cast< u8* >( block ) + 8;
-
- // create the minimal point set
- ColourSet colours( rgba, mask, flags );
-
- // check the compression type and compress colour
- if( colours.GetCount() == 1 )
- {
- // always do a single colour fit
- SingleColourFit fit( &colours, flags );
- fit.Compress( colourBlock );
- }
- else if( ( flags & kColourRangeFit ) != 0 || colours.GetCount() == 0 )
- {
- // do a range fit
- RangeFit fit( &colours, flags, metric );
- fit.Compress( colourBlock );
- }
- else
- {
- // default to a cluster fit (could be iterative or not)
- ClusterFit fit( &colours, flags, metric );
- fit.Compress( colourBlock );
- }
-
- // compress alpha separately if necessary
- if( ( flags & kDxt3 ) != 0 )
- CompressAlphaDxt3( rgba, mask, alphaBlock );
- else if( ( flags & kDxt5 ) != 0 )
- CompressAlphaDxt5( rgba, mask, alphaBlock );
-}
-
-void Decompress( u8* rgba, void const* block, int flags )
-{
- // fix any bad flags
- flags = FixFlags( flags );
-
- // get the block locations
- void const* colourBlock = block;
- void const* alphaBlock = block;
- if( ( flags & ( kDxt3 | kDxt5 ) ) != 0 )
- colourBlock = reinterpret_cast< u8 const* >( block ) + 8;
-
- // decompress colour
- // -- GODOT start --
- //DecompressColour( rgba, colourBlock, ( flags & kDxt1 ) != 0 );
- if(( flags & ( kBc4 ) ) != 0)
- DecompressColourBc4( rgba, colourBlock);
- else if(( flags & ( kBc5 ) ) != 0)
- DecompressColourBc5( rgba, colourBlock);
- else
- DecompressColour( rgba, colourBlock, ( flags & kDxt1 ) != 0 );
- // -- GODOT end --
-
- // decompress alpha separately if necessary
- if( ( flags & kDxt3 ) != 0 )
- DecompressAlphaDxt3( rgba, alphaBlock );
- else if( ( flags & kDxt5 ) != 0 )
- DecompressAlphaDxt5( rgba, alphaBlock );
-}
-
-int GetStorageRequirements( int width, int height, int flags )
-{
- // fix any bad flags
- flags = FixFlags( flags );
-
- // compute the storage requirements
- int blockcount = ( ( width + 3 )/4 ) * ( ( height + 3 )/4 );
- int blocksize = ( ( flags & ( kDxt1 | kBc4 ) ) != 0 ) ? 8 : 16;
- return blockcount*blocksize;
-}
-
-void CopyRGBA( u8 const* source, u8* dest, int flags )
-{
- if (flags & kSourceBGRA)
- {
- // convert from bgra to rgba
- dest[0] = source[2];
- dest[1] = source[1];
- dest[2] = source[0];
- dest[3] = source[3];
- }
- else
- {
- for( int i = 0; i < 4; ++i )
- *dest++ = *source++;
- }
-}
-
-void CompressImage( u8 const* rgba, int width, int height, int pitch, void* blocks, int flags, float* metric )
-{
- // fix any bad flags
- flags = FixFlags( flags );
-
- // loop over blocks
-#ifdef SQUISH_USE_OPENMP
-# pragma omp parallel for
-#endif
- for( int y = 0; y < height; y += 4 )
- {
- // initialise the block output
- u8* targetBlock = reinterpret_cast< u8* >( blocks );
- int bytesPerBlock = ( ( flags & ( kDxt1 | kBc4 ) ) != 0 ) ? 8 : 16;
- targetBlock += ( (y / 4) * ( (width + 3) / 4) ) * bytesPerBlock;
-
- for( int x = 0; x < width; x += 4 )
- {
- // build the 4x4 block of pixels
- u8 sourceRgba[16*4];
- u8* targetPixel = sourceRgba;
- int mask = 0;
- for( int py = 0; py < 4; ++py )
- {
- for( int px = 0; px < 4; ++px )
- {
- // get the source pixel in the image
- int sx = x + px;
- int sy = y + py;
-
- // enable if we're in the image
- if( sx < width && sy < height )
- {
- // copy the rgba value
- u8 const* sourcePixel = rgba + pitch*sy + 4*sx;
- CopyRGBA(sourcePixel, targetPixel, flags);
- // enable this pixel
- mask |= ( 1 << ( 4*py + px ) );
- }
-
- // advance to the next pixel
- targetPixel += 4;
- }
- }
-
- // compress it into the output
- CompressMasked( sourceRgba, mask, targetBlock, flags, metric );
-
- // advance
- targetBlock += bytesPerBlock;
- }
- }
-}
-
-void CompressImage( u8 const* rgba, int width, int height, void* blocks, int flags, float* metric )
-{
- CompressImage(rgba, width, height, width*4, blocks, flags, metric);
-}
-
-void DecompressImage( u8* rgba, int width, int height, int pitch, void const* blocks, int flags )
-{
- // fix any bad flags
- flags = FixFlags( flags );
-
- // loop over blocks
-#ifdef SQUISH_USE_OPENMP
-# pragma omp parallel for
-#endif
- for( int y = 0; y < height; y += 4 )
- {
- // initialise the block input
- u8 const* sourceBlock = reinterpret_cast< u8 const* >( blocks );
- int bytesPerBlock = ( ( flags & ( kDxt1 | kBc4 ) ) != 0 ) ? 8 : 16;
- sourceBlock += ( (y / 4) * ( (width + 3) / 4) ) * bytesPerBlock;
-
- for( int x = 0; x < width; x += 4 )
- {
- // decompress the block
- u8 targetRgba[4*16];
- Decompress( targetRgba, sourceBlock, flags );
-
- // write the decompressed pixels to the correct image locations
- u8 const* sourcePixel = targetRgba;
- for( int py = 0; py < 4; ++py )
- {
- for( int px = 0; px < 4; ++px )
- {
- // get the target location
- int sx = x + px;
- int sy = y + py;
-
- // write if we're in the image
- if( sx < width && sy < height )
- {
- // copy the rgba value
- u8* targetPixel = rgba + pitch*sy + 4*sx;
- CopyRGBA(sourcePixel, targetPixel, flags);
- }
-
- // advance to the next pixel
- sourcePixel += 4;
- }
- }
-
- // advance
- sourceBlock += bytesPerBlock;
- }
- }
-}
-
-void DecompressImage( u8* rgba, int width, int height, void const* blocks, int flags )
-{
- DecompressImage( rgba, width, height, width*4, blocks, flags );
-}
-
-static double ErrorSq(double x, double y)
-{
- return (x - y) * (x - y);
-}
-
-static void ComputeBlockWMSE(u8 const *original, u8 const *compressed, unsigned int w, unsigned int h, double &cmse, double &amse)
-{
- // Computes the MSE for the block and weights it by the variance of the original block.
- // If the variance of the original block is less than 4 (i.e. a standard deviation of 1 per channel)
- // then the block is close to being a single colour. Quantisation errors in single colour blocks
- // are easier to see than similar errors in blocks that contain more colours, particularly when there
- // are many such blocks in a large area (eg a blue sky background) as they cause banding. Given that
- // banding is easier to see than small errors in "complex" blocks, we weight the errors by a factor
- // of 5. This implies that images with large, single colour areas will have a higher potential WMSE
- // than images with lots of detail.
-
- cmse = amse = 0;
- unsigned int sum_p[4]; // per channel sum of pixels
- unsigned int sum_p2[4]; // per channel sum of pixels squared
- memset(sum_p, 0, sizeof(sum_p));
- memset(sum_p2, 0, sizeof(sum_p2));
- for( unsigned int py = 0; py < 4; ++py )
- {
- for( unsigned int px = 0; px < 4; ++px )
- {
- if( px < w && py < h )
- {
- double pixelCMSE = 0;
- for( int i = 0; i < 3; ++i )
- {
- pixelCMSE += ErrorSq(original[i], compressed[i]);
- sum_p[i] += original[i];
- sum_p2[i] += (unsigned int)original[i]*original[i];
- }
- if( original[3] == 0 && compressed[3] == 0 )
- pixelCMSE = 0; // transparent in both, so colour is inconsequential
- amse += ErrorSq(original[3], compressed[3]);
- cmse += pixelCMSE;
- sum_p[3] += original[3];
- sum_p2[3] += (unsigned int)original[3]*original[3];
- }
- original += 4;
- compressed += 4;
- }
- }
- unsigned int variance = 0;
- for( int i = 0; i < 4; ++i )
- variance += w*h*sum_p2[i] - sum_p[i]*sum_p[i];
- if( variance < 4 * w * w * h * h )
- {
- amse *= 5;
- cmse *= 5;
- }
-}
-
-void ComputeMSE( u8 const *rgba, int width, int height, int pitch, u8 const *dxt, int flags, double &colourMSE, double &alphaMSE )
-{
- // fix any bad flags
- flags = FixFlags( flags );
- colourMSE = alphaMSE = 0;
-
- // initialise the block input
- squish::u8 const* sourceBlock = dxt;
- int bytesPerBlock = ( ( flags & squish::kDxt1 ) != 0 ) ? 8 : 16;
-
- // loop over blocks
- for( int y = 0; y < height; y += 4 )
- {
- for( int x = 0; x < width; x += 4 )
- {
- // decompress the block
- u8 targetRgba[4*16];
- Decompress( targetRgba, sourceBlock, flags );
- u8 const* sourcePixel = targetRgba;
-
- // copy across to a similar pixel block
- u8 originalRgba[4*16];
- u8* originalPixel = originalRgba;
-
- for( int py = 0; py < 4; ++py )
- {
- for( int px = 0; px < 4; ++px )
- {
- int sx = x + px;
- int sy = y + py;
- if( sx < width && sy < height )
- {
- u8 const* targetPixel = rgba + pitch*sy + 4*sx;
- CopyRGBA(targetPixel, originalPixel, flags);
- }
- sourcePixel += 4;
- originalPixel += 4;
- }
- }
-
- // compute the weighted MSE of the block
- double blockCMSE, blockAMSE;
- ComputeBlockWMSE(originalRgba, targetRgba, std::min(4, width - x), std::min(4, height - y), blockCMSE, blockAMSE);
- colourMSE += blockCMSE;
- alphaMSE += blockAMSE;
- // advance
- sourceBlock += bytesPerBlock;
- }
- }
- colourMSE /= (width * height * 3);
- alphaMSE /= (width * height);
-}
-
-void ComputeMSE( u8 const *rgba, int width, int height, u8 const *dxt, int flags, double &colourMSE, double &alphaMSE )
-{
- ComputeMSE(rgba, width, height, width*4, dxt, flags, colourMSE, alphaMSE);
-}
-
-} // namespace squish
diff --git a/thirdparty/squish/squish.h b/thirdparty/squish/squish.h
deleted file mode 100644
index 14c9bb59fb..0000000000
--- a/thirdparty/squish/squish.h
+++ /dev/null
@@ -1,309 +0,0 @@
-/* -----------------------------------------------------------------------------
-
- Copyright (c) 2006 Simon Brown si@sjbrown.co.uk
-
- 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 SQUISH_H
-#define SQUISH_H
-
-//! All squish API functions live in this namespace.
-namespace squish {
-
-// -----------------------------------------------------------------------------
-
-//! Typedef a quantity that is a single unsigned byte.
-typedef unsigned char u8;
-
-// -----------------------------------------------------------------------------
-
-enum
-{
- //! Use DXT1 compression.
- kDxt1 = ( 1 << 0 ),
-
- //! Use DXT3 compression.
- kDxt3 = ( 1 << 1 ),
-
- //! Use DXT5 compression.
- kDxt5 = ( 1 << 2 ),
-
- //! Use BC4 compression.
- kBc4 = ( 1 << 3 ),
-
- //! Use BC5 compression.
- kBc5 = ( 1 << 4 ),
-
- //! Use a slow but high quality colour compressor (the default).
- kColourClusterFit = ( 1 << 5 ),
-
- //! Use a fast but low quality colour compressor.
- kColourRangeFit = ( 1 << 6 ),
-
- //! Weight the colour by alpha during cluster fit (disabled by default).
- kWeightColourByAlpha = ( 1 << 7 ),
-
- //! Use a very slow but very high quality colour compressor.
- kColourIterativeClusterFit = ( 1 << 8 ),
-
- //! Source is BGRA rather than RGBA
- kSourceBGRA = ( 1 << 9 )
-};
-
-// -----------------------------------------------------------------------------
-
-/*! @brief Compresses a 4x4 block of pixels.
-
- @param rgba The rgba values of the 16 source pixels.
- @param mask The valid pixel mask.
- @param block Storage for the compressed DXT block.
- @param flags Compression flags.
- @param metric An optional perceptual metric.
-
- The source pixels should be presented as a contiguous array of 16 rgba
- values, with each component as 1 byte each. In memory this should be:
-
- { r1, g1, b1, a1, .... , r16, g16, b16, a16 }
-
- The mask parameter enables only certain pixels within the block. The lowest
- bit enables the first pixel and so on up to the 16th bit. Bits beyond the
- 16th bit are ignored. Pixels that are not enabled are allowed to take
- arbitrary colours in the output block. An example of how this can be used
- is in the CompressImage function to disable pixels outside the bounds of
- the image when the width or height is not divisible by 4.
-
- The flags parameter should specify kDxt1, kDxt3, kDxt5, kBc4, or kBc5 compression,
- however, DXT1 will be used by default if none is specified. When using DXT1
- compression, 8 bytes of storage are required for the compressed DXT block.
- DXT3 and DXT5 compression require 16 bytes of storage per block.
-
- The flags parameter can also specify a preferred colour compressor to use
- when fitting the RGB components of the data. Possible colour compressors
- are: kColourClusterFit (the default), kColourRangeFit (very fast, low
- quality) or kColourIterativeClusterFit (slowest, best quality).
-
- When using kColourClusterFit or kColourIterativeClusterFit, an additional
- flag can be specified to weight the importance of each pixel by its alpha
- value. For images that are rendered using alpha blending, this can
- significantly increase the perceived quality.
-
- The metric parameter can be used to weight the relative importance of each
- colour channel, or pass NULL to use the default uniform weight of
- { 1.0f, 1.0f, 1.0f }. This replaces the previous flag-based control that
- allowed either uniform or "perceptual" weights with the fixed values
- { 0.2126f, 0.7152f, 0.0722f }. If non-NULL, the metric should point to a
- contiguous array of 3 floats.
-*/
-void CompressMasked( u8 const* rgba, int mask, void* block, int flags, float* metric = 0 );
-
-// -----------------------------------------------------------------------------
-
-/*! @brief Compresses a 4x4 block of pixels.
-
- @param rgba The rgba values of the 16 source pixels.
- @param block Storage for the compressed DXT block.
- @param flags Compression flags.
- @param metric An optional perceptual metric.
-
- The source pixels should be presented as a contiguous array of 16 rgba
- values, with each component as 1 byte each. In memory this should be:
-
- { r1, g1, b1, a1, .... , r16, g16, b16, a16 }
-
- The flags parameter should specify kDxt1, kDxt3, kDxt5, kBc4, or kBc5 compression,
- however, DXT1 will be used by default if none is specified. When using DXT1
- compression, 8 bytes of storage are required for the compressed DXT block.
- DXT3 and DXT5 compression require 16 bytes of storage per block.
-
- The flags parameter can also specify a preferred colour compressor to use
- when fitting the RGB components of the data. Possible colour compressors
- are: kColourClusterFit (the default), kColourRangeFit (very fast, low
- quality) or kColourIterativeClusterFit (slowest, best quality).
-
- When using kColourClusterFit or kColourIterativeClusterFit, an additional
- flag can be specified to weight the importance of each pixel by its alpha
- value. For images that are rendered using alpha blending, this can
- significantly increase the perceived quality.
-
- The metric parameter can be used to weight the relative importance of each
- colour channel, or pass NULL to use the default uniform weight of
- { 1.0f, 1.0f, 1.0f }. This replaces the previous flag-based control that
- allowed either uniform or "perceptual" weights with the fixed values
- { 0.2126f, 0.7152f, 0.0722f }. If non-NULL, the metric should point to a
- contiguous array of 3 floats.
-
- This method is an inline that calls CompressMasked with a mask of 0xffff,
- provided for compatibility with older versions of squish.
-*/
-inline void Compress( u8 const* rgba, void* block, int flags, float* metric = 0 )
-{
- CompressMasked( rgba, 0xffff, block, flags, metric );
-}
-
-// -----------------------------------------------------------------------------
-
-/*! @brief Decompresses a 4x4 block of pixels.
-
- @param rgba Storage for the 16 decompressed pixels.
- @param block The compressed DXT block.
- @param flags Compression flags.
-
- The decompressed pixels will be written as a contiguous array of 16 rgba
- values, with each component as 1 byte each. In memory this is:
-
- { r1, g1, b1, a1, .... , r16, g16, b16, a16 }
-
- The flags parameter should specify kDxt1, kDxt3, kDxt5, kBc4, or kBc5 compression,
- however, DXT1 will be used by default if none is specified. All other flags
- are ignored.
-*/
-void Decompress( u8* rgba, void const* block, int flags );
-
-// -----------------------------------------------------------------------------
-
-/*! @brief Computes the amount of compressed storage required.
-
- @param width The width of the image.
- @param height The height of the image.
- @param flags Compression flags.
-
- The flags parameter should specify kDxt1, kDxt3, kDxt5, kBc4, or kBc5 compression,
- however, DXT1 will be used by default if none is specified. All other flags
- are ignored.
-
- Most DXT images will be a multiple of 4 in each dimension, but this
- function supports arbitrary size images by allowing the outer blocks to
- be only partially used.
-*/
-int GetStorageRequirements( int width, int height, int flags );
-
-// -----------------------------------------------------------------------------
-
-/*! @brief Compresses an image in memory.
-
- @param rgba The pixels of the source.
- @param width The width of the source image.
- @param height The height of the source image.
- @param pitch The pitch of the source image.
- @param blocks Storage for the compressed output.
- @param flags Compression flags.
- @param metric An optional perceptual metric.
-
- The source pixels should be presented as a contiguous array of width*height
- rgba values, with each component as 1 byte each. In memory this should be:
-
- { r1, g1, b1, a1, .... , rn, gn, bn, an } for n = width*height
-
- The flags parameter should specify kDxt1, kDxt3, kDxt5, kBc4, or kBc5 compression,
- however, DXT1 will be used by default if none is specified. When using DXT1
- compression, 8 bytes of storage are required for each compressed DXT block.
- DXT3 and DXT5 compression require 16 bytes of storage per block.
-
- The flags parameter can also specify a preferred colour compressor to use
- when fitting the RGB components of the data. Possible colour compressors
- are: kColourClusterFit (the default), kColourRangeFit (very fast, low
- quality) or kColourIterativeClusterFit (slowest, best quality).
-
- When using kColourClusterFit or kColourIterativeClusterFit, an additional
- flag can be specified to weight the importance of each pixel by its alpha
- value. For images that are rendered using alpha blending, this can
- significantly increase the perceived quality.
-
- The metric parameter can be used to weight the relative importance of each
- colour channel, or pass NULL to use the default uniform weight of
- { 1.0f, 1.0f, 1.0f }. This replaces the previous flag-based control that
- allowed either uniform or "perceptual" weights with the fixed values
- { 0.2126f, 0.7152f, 0.0722f }. If non-NULL, the metric should point to a
- contiguous array of 3 floats.
-
- Internally this function calls squish::CompressMasked for each block, which
- allows for pixels outside the image to take arbitrary values. The function
- squish::GetStorageRequirements can be called to compute the amount of memory
- to allocate for the compressed output.
-
- Note on compression quality: When compressing textures with
- libsquish it is recommended to apply a gamma-correction
- beforehand. This will reduce the blockiness in dark areas. The
- level of necessary gamma-correction is platform dependent. For
- example, a gamma correction with gamma = 0.5 before compression
- and gamma = 2.0 after decompression yields good results on the
- Windows platform but for other platforms like MacOS X a different
- gamma value may be more suitable.
-*/
-void CompressImage( u8 const* rgba, int width, int height, int pitch, void* blocks, int flags, float* metric = 0 );
-void CompressImage( u8 const* rgba, int width, int height, void* blocks, int flags, float* metric = 0 );
-
-// -----------------------------------------------------------------------------
-
-/*! @brief Decompresses an image in memory.
-
- @param rgba Storage for the decompressed pixels.
- @param width The width of the source image.
- @param height The height of the source image.
- @param pitch The pitch of the decompressed pixels.
- @param blocks The compressed DXT blocks.
- @param flags Compression flags.
-
- The decompressed pixels will be written as a contiguous array of width*height
- 16 rgba values, with each component as 1 byte each. In memory this is:
-
- { r1, g1, b1, a1, .... , rn, gn, bn, an } for n = width*height
-
- The flags parameter should specify kDxt1, kDxt3, kDxt5, kBc4, or kBc5 compression,
- however, DXT1 will be used by default if none is specified. All other flags
- are ignored.
-
- Internally this function calls squish::Decompress for each block.
-*/
-void DecompressImage( u8* rgba, int width, int height, int pitch, void const* blocks, int flags );
-void DecompressImage( u8* rgba, int width, int height, void const* blocks, int flags );
-
-// -----------------------------------------------------------------------------
-
-/*! @brief Computes MSE of an compressed image in memory.
-
- @param rgba The original image pixels.
- @param width The width of the source image.
- @param height The height of the source image.
- @param pitch The pitch of the source image.
- @param dxt The compressed dxt blocks
- @param flags Compression flags.
- @param colourMSE The MSE of the colour values.
- @param alphaMSE The MSE of the alpha values.
-
- The colour MSE and alpha MSE are computed across all pixels. The colour MSE is
- averaged across all rgb values (i.e. colourMSE = sum sum_k ||dxt.k - rgba.k||/3)
-
- The flags parameter should specify kDxt1, kDxt3, kDxt5, kBc4, or kBc5 compression,
- however, DXT1 will be used by default if none is specified. All other flags
- are ignored.
-
- Internally this function calls squish::Decompress for each block.
-*/
-void ComputeMSE(u8 const *rgba, int width, int height, int pitch, u8 const *dxt, int flags, double &colourMSE, double &alphaMSE);
-void ComputeMSE(u8 const *rgba, int width, int height, u8 const *dxt, int flags, double &colourMSE, double &alphaMSE);
-
-// -----------------------------------------------------------------------------
-
-} // namespace squish
-
-#endif // ndef SQUISH_H