diff options
147 files changed, 20203 insertions, 14102 deletions
diff --git a/doc/classes/Camera3D.xml b/doc/classes/Camera3D.xml index 2ada0c556d..8a91a91b22 100644 --- a/doc/classes/Camera3D.xml +++ b/doc/classes/Camera3D.xml @@ -29,11 +29,11 @@ Gets the camera transform. Subclassed cameras such as [ClippedCamera3D] may provide different transforms than the [Node] transform. </description> </method> - <method name="get_cull_mask_bit" qualifiers="const"> + <method name="get_cull_mask_value" qualifiers="const"> <return type="bool" /> - <argument index="0" name="layer" type="int" /> + <argument index="0" name="layer_number" type="int" /> <description> - Returns [code]true[/code] if the given [code]layer[/code] in the [member cull_mask] is enabled, [code]false[/code] otherwise. + Returns whether or not the specified layer of the [member cull_mask] is enabled, given a [code]layer_number[/code] between 1 and 20. </description> </method> <method name="get_frustum" qualifiers="const"> @@ -92,12 +92,12 @@ Returns a 3D position in world space, that is the result of projecting a point on the [Viewport] rectangle by the camera projection. This is useful for casting rays in the form of (origin, normal) for object intersection or picking. </description> </method> - <method name="set_cull_mask_bit"> + <method name="set_cull_mask_value"> <return type="void" /> - <argument index="0" name="layer" type="int" /> - <argument index="1" name="enable" type="bool" /> + <argument index="0" name="layer_number" type="int" /> + <argument index="1" name="value" type="bool" /> <description> - Enables or disables the given [code]layer[/code] in the [member cull_mask]. + Based on [code]value[/code], enables or disables the specified layer in the [member cull_mask], given a [code]layer_number[/code] between 1 and 20. </description> </method> <method name="set_frustum"> diff --git a/doc/classes/ClippedCamera3D.xml b/doc/classes/ClippedCamera3D.xml index 1a76412826..1a0d3499cd 100644 --- a/doc/classes/ClippedCamera3D.xml +++ b/doc/classes/ClippedCamera3D.xml @@ -35,12 +35,11 @@ Returns the distance the camera has been offset due to a collision. </description> </method> - <method name="get_collision_mask_bit" qualifiers="const"> + <method name="get_collision_mask_value" qualifiers="const"> <return type="bool" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <description> - Returns [code]true[/code] if the specified bit index is on. - [b]Note:[/b] Bit indices range from 0-19. + Returns whether or not the specified layer of the [member collision_mask] is enabled, given a [code]layer_number[/code] between 1 and 32. </description> </method> <method name="remove_exception"> @@ -57,13 +56,12 @@ Removes a collision exception with the specified [RID]. </description> </method> - <method name="set_collision_mask_bit"> + <method name="set_collision_mask_value"> <return type="void" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <argument index="1" name="value" type="bool" /> <description> - Sets the specified bit index to the [code]value[/code]. - [b]Note:[/b] Bit indices range from 0-19. + Based on [code]value[/code], enables or disables the specified layer in the [member collision_mask], given a [code]layer_number[/code] between 1 and 32. </description> </method> </methods> diff --git a/doc/classes/CollisionObject2D.xml b/doc/classes/CollisionObject2D.xml index 6bb756ea2c..7129c72e7c 100644 --- a/doc/classes/CollisionObject2D.xml +++ b/doc/classes/CollisionObject2D.xml @@ -25,18 +25,18 @@ Creates a new shape owner for the given object. Returns [code]owner_id[/code] of the new owner for future reference. </description> </method> - <method name="get_collision_layer_bit" qualifiers="const"> + <method name="get_collision_layer_value" qualifiers="const"> <return type="bool" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <description> - Returns whether or not the specified [code]bit[/code] of the [member collision_layer] is set. + Returns whether or not the specified layer of the [member collision_layer] is enabled, given a [code]layer_number[/code] between 1 and 32. </description> </method> - <method name="get_collision_mask_bit" qualifiers="const"> + <method name="get_collision_mask_value" qualifiers="const"> <return type="bool" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <description> - Returns whether or not the specified [code]bit[/code] of the [member collision_mask] is set. + Returns whether or not the specified layer of the [member collision_mask] is enabled, given a [code]layer_number[/code] between 1 and 32. </description> </method> <method name="get_rid" qualifiers="const"> @@ -79,22 +79,20 @@ Removes the given shape owner. </description> </method> - <method name="set_collision_layer_bit"> + <method name="set_collision_layer_value"> <return type="void" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <argument index="1" name="value" type="bool" /> <description> - If [code]value[/code] is [code]true[/code], sets the specified [code]bit[/code] in the the [member collision_layer]. - If [code]value[/code] is [code]false[/code], clears the specified [code]bit[/code] in the the [member collision_layer]. + Based on [code]value[/code], enables or disables the specified layer in the [member collision_layer], given a [code]layer_number[/code] between 1 and 32. </description> </method> - <method name="set_collision_mask_bit"> + <method name="set_collision_mask_value"> <return type="void" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <argument index="1" name="value" type="bool" /> <description> - If [code]value[/code] is [code]true[/code], sets the specified [code]bit[/code] in the the [member collision_mask]. - If [code]value[/code] is [code]false[/code], clears the specified [code]bit[/code] in the the [member collision_mask]. + Based on [code]value[/code], enables or disables the specified layer in the [member collision_mask], given a [code]layer_number[/code] between 1 and 32. </description> </method> <method name="shape_find_owner" qualifiers="const"> diff --git a/doc/classes/CollisionObject3D.xml b/doc/classes/CollisionObject3D.xml index 0210f6297f..f9151a2c2f 100644 --- a/doc/classes/CollisionObject3D.xml +++ b/doc/classes/CollisionObject3D.xml @@ -27,18 +27,18 @@ Creates a new shape owner for the given object. Returns [code]owner_id[/code] of the new owner for future reference. </description> </method> - <method name="get_collision_layer_bit" qualifiers="const"> + <method name="get_collision_layer_value" qualifiers="const"> <return type="bool" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <description> - Returns whether or not the specified [code]bit[/code] of the [member collision_layer] is set. + Returns whether or not the specified layer of the [member collision_layer] is enabled, given a [code]layer_number[/code] between 1 and 32. </description> </method> - <method name="get_collision_mask_bit" qualifiers="const"> + <method name="get_collision_mask_value" qualifiers="const"> <return type="bool" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <description> - Returns whether or not the specified [code]bit[/code] of the [member collision_mask] is set. + Returns whether or not the specified layer of the [member collision_mask] is enabled, given a [code]layer_number[/code] between 1 and 32. </description> </method> <method name="get_rid" qualifiers="const"> @@ -67,22 +67,20 @@ Removes the given shape owner. </description> </method> - <method name="set_collision_layer_bit"> + <method name="set_collision_layer_value"> <return type="void" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <argument index="1" name="value" type="bool" /> <description> - If [code]value[/code] is [code]true[/code], sets the specified [code]bit[/code] in the the [member collision_layer]. - If [code]value[/code] is [code]false[/code], clears the specified [code]bit[/code] in the the [member collision_layer]. + Based on [code]value[/code], enables or disables the specified layer in the [member collision_layer], given a [code]layer_number[/code] between 1 and 32. </description> </method> - <method name="set_collision_mask_bit"> + <method name="set_collision_mask_value"> <return type="void" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <argument index="1" name="value" type="bool" /> <description> - If [code]value[/code] is [code]true[/code], sets the specified [code]bit[/code] in the the [member collision_mask]. - If [code]value[/code] is [code]false[/code], clears the specified [code]bit[/code] in the the [member collision_mask]. + Based on [code]value[/code], enables or disables the specified layer in the [member collision_mask], given a [code]layer_number[/code] between 1 and 32. </description> </method> <method name="shape_find_owner" qualifiers="const"> diff --git a/doc/classes/NavigationMesh.xml b/doc/classes/NavigationMesh.xml index 89a394ef6c..e476949360 100644 --- a/doc/classes/NavigationMesh.xml +++ b/doc/classes/NavigationMesh.xml @@ -30,11 +30,11 @@ Initializes the navigation mesh by setting the vertices and indices according to a [Mesh]. </description> </method> - <method name="get_collision_mask_bit" qualifiers="const"> + <method name="get_collision_mask_value" qualifiers="const"> <return type="bool" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <description> - Returns whether the specified [code]bit[/code] of the [member geometry/collision_mask] is set. + Returns whether or not the specified layer of the [member geometry/collision_mask] is enabled, given a [code]layer_number[/code] between 1 and 32. </description> </method> <method name="get_polygon"> @@ -56,13 +56,12 @@ Returns a [PackedVector3Array] containing all the vertices being used to create the polygons. </description> </method> - <method name="set_collision_mask_bit"> + <method name="set_collision_mask_value"> <return type="void" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <argument index="1" name="value" type="bool" /> <description> - If [code]value[/code] is [code]true[/code], sets the specified [code]bit[/code] in the [member geometry/collision_mask]. - If [code]value[/code] is [code]false[/code], clears the specified [code]bit[/code] in the [member geometry/collision_mask]. + Based on [code]value[/code], enables or disables the specified layer in the [member geometry/collision_mask], given a [code]layer_number[/code] between 1 and 32. </description> </method> <method name="set_vertices"> diff --git a/doc/classes/OccluderInstance3D.xml b/doc/classes/OccluderInstance3D.xml index 150bfd9257..cc4bddc229 100644 --- a/doc/classes/OccluderInstance3D.xml +++ b/doc/classes/OccluderInstance3D.xml @@ -7,17 +7,19 @@ <tutorials> </tutorials> <methods> - <method name="get_bake_mask_bit" qualifiers="const"> + <method name="get_bake_mask_value" qualifiers="const"> <return type="bool" /> - <argument index="0" name="layer" type="int" /> + <argument index="0" name="layer_number" type="int" /> <description> + Returns whether or not the specified layer of the [member bake_mask] is enabled, given a [code]layer_number[/code] between 1 and 20. </description> </method> - <method name="set_bake_mask_bit"> + <method name="set_bake_mask_value"> <return type="void" /> - <argument index="0" name="layer" type="int" /> - <argument index="1" name="enabled" type="bool" /> + <argument index="0" name="layer_number" type="int" /> + <argument index="1" name="value" type="bool" /> <description> + Based on [code]value[/code], enables or disables the specified layer in the [member bake_mask], given a [code]layer_number[/code] between 1 and 20. </description> </method> </methods> diff --git a/doc/classes/PhysicsDirectSpaceState2D.xml b/doc/classes/PhysicsDirectSpaceState2D.xml index 2a32bc1cb9..e84b3e0e49 100644 --- a/doc/classes/PhysicsDirectSpaceState2D.xml +++ b/doc/classes/PhysicsDirectSpaceState2D.xml @@ -47,7 +47,7 @@ <argument index="0" name="point" type="Vector2" /> <argument index="1" name="max_results" type="int" default="32" /> <argument index="2" name="exclude" type="Array" default="[]" /> - <argument index="3" name="collision_layer" type="int" default="2147483647" /> + <argument index="3" name="collision_mask" type="int" default="2147483647" /> <argument index="4" name="collide_with_bodies" type="bool" default="true" /> <argument index="5" name="collide_with_areas" type="bool" default="false" /> <description> @@ -57,7 +57,7 @@ [code]metadata[/code]: The intersecting shape's metadata. This metadata is different from [method Object.get_meta], and is set with [method PhysicsServer2D.shape_set_data]. [code]rid[/code]: The intersecting object's [RID]. [code]shape[/code]: The shape index of the colliding shape. - Additionally, the method can take an [code]exclude[/code] array of objects or [RID]s that are to be excluded from collisions, a [code]collision_mask[/code] bitmask representing the physics layers to check in, or booleans to determine if the ray should collide with [PhysicsBody2D]s or [Area2D]s, respectively. + Additionally, the method can take an [code]exclude[/code] array of objects or [RID]s that are to be excluded from collisions, a [code]collision_mask[/code] bitmask representing the physics layers to detect, or booleans to determine if the ray should collide with [PhysicsBody2D]s or [Area2D]s, respectively. [b]Note:[/b] [ConcavePolygonShape2D]s and [CollisionPolygon2D]s in [code]Segments[/code] build mode are not solid shapes. Therefore, they will not be detected. </description> </method> @@ -67,7 +67,7 @@ <argument index="1" name="canvas_instance_id" type="int" /> <argument index="2" name="max_results" type="int" default="32" /> <argument index="3" name="exclude" type="Array" default="[]" /> - <argument index="4" name="collision_layer" type="int" default="2147483647" /> + <argument index="4" name="collision_mask" type="int" default="2147483647" /> <argument index="5" name="collide_with_bodies" type="bool" default="true" /> <argument index="6" name="collide_with_areas" type="bool" default="false" /> <description> @@ -78,7 +78,7 @@ <argument index="0" name="from" type="Vector2" /> <argument index="1" name="to" type="Vector2" /> <argument index="2" name="exclude" type="Array" default="[]" /> - <argument index="3" name="collision_layer" type="int" default="2147483647" /> + <argument index="3" name="collision_mask" type="int" default="2147483647" /> <argument index="4" name="collide_with_bodies" type="bool" default="true" /> <argument index="5" name="collide_with_areas" type="bool" default="false" /> <description> @@ -91,7 +91,7 @@ [code]rid[/code]: The intersecting object's [RID]. [code]shape[/code]: The shape index of the colliding shape. If the ray did not intersect anything, then an empty dictionary is returned instead. - Additionally, the method can take an [code]exclude[/code] array of objects or [RID]s that are to be excluded from collisions, a [code]collision_mask[/code] bitmask representing the physics layers to check in, or booleans to determine if the ray should collide with [PhysicsBody2D]s or [Area2D]s, respectively. + Additionally, the method can take an [code]exclude[/code] array of objects or [RID]s that are to be excluded from collisions, a [code]collision_mask[/code] bitmask representing the physics layers to detect, or booleans to determine if the ray should collide with [PhysicsBody2D]s or [Area2D]s, respectively. </description> </method> <method name="intersect_shape"> diff --git a/doc/classes/PhysicsDirectSpaceState3D.xml b/doc/classes/PhysicsDirectSpaceState3D.xml index a6bfc8754d..13db50a2c7 100644 --- a/doc/classes/PhysicsDirectSpaceState3D.xml +++ b/doc/classes/PhysicsDirectSpaceState3D.xml @@ -59,7 +59,7 @@ [code]rid[/code]: The intersecting object's [RID]. [code]shape[/code]: The shape index of the colliding shape. If the ray did not intersect anything, then an empty dictionary is returned instead. - Additionally, the method can take an [code]exclude[/code] array of objects or [RID]s that are to be excluded from collisions, a [code]collision_mask[/code] bitmask representing the physics layers to check in, or booleans to determine if the ray should collide with [PhysicsBody3D]s or [Area3D]s, respectively. + Additionally, the method can take an [code]exclude[/code] array of objects or [RID]s that are to be excluded from collisions, a [code]collision_mask[/code] bitmask representing the physics layers to detect, or booleans to determine if the ray should collide with [PhysicsBody3D]s or [Area3D]s, respectively. </description> </method> <method name="intersect_shape"> diff --git a/doc/classes/PhysicsShapeQueryParameters2D.xml b/doc/classes/PhysicsShapeQueryParameters2D.xml index 229a40638a..8b006c68e7 100644 --- a/doc/classes/PhysicsShapeQueryParameters2D.xml +++ b/doc/classes/PhysicsShapeQueryParameters2D.xml @@ -17,8 +17,8 @@ <member name="collide_with_bodies" type="bool" setter="set_collide_with_bodies" getter="is_collide_with_bodies_enabled" default="true"> If [code]true[/code], the query will take [PhysicsBody2D]s into account. </member> - <member name="collision_layer" type="int" setter="set_collision_layer" getter="get_collision_layer" default="2147483647"> - The physics layer(s) the query will take into account (as a bitmask). See [url=https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html#collision-layers-and-masks]Collision layers and masks[/url] in the documentation for more information. + <member name="collision_mask" type="int" setter="set_collision_mask" getter="get_collision_mask" default="2147483647"> + The physics layers the query will detect (as a bitmask). By default, all collision layers are detected. See [url=https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html#collision-layers-and-masks]Collision layers and masks[/url] in the documentation for more information. </member> <member name="exclude" type="Array" setter="set_exclude" getter="get_exclude" default="[]"> The list of objects or object [RID]s that will be excluded from collisions. diff --git a/doc/classes/PhysicsShapeQueryParameters3D.xml b/doc/classes/PhysicsShapeQueryParameters3D.xml index 9ca892acb3..de9b623591 100644 --- a/doc/classes/PhysicsShapeQueryParameters3D.xml +++ b/doc/classes/PhysicsShapeQueryParameters3D.xml @@ -18,7 +18,7 @@ If [code]true[/code], the query will take [PhysicsBody3D]s into account. </member> <member name="collision_mask" type="int" setter="set_collision_mask" getter="get_collision_mask" default="2147483647"> - The physics layer(s) the query will take into account (as a bitmask). See [url=https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html#collision-layers-and-masks]Collision layers and masks[/url] in the documentation for more information. + The physics layers the query will detect (as a bitmask). By default, all collision layers are detected. See [url=https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html#collision-layers-and-masks]Collision layers and masks[/url] in the documentation for more information. </member> <member name="exclude" type="Array" setter="set_exclude" getter="get_exclude" default="[]"> The list of objects or object [RID]s that will be excluded from collisions. diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 24caceec1c..f769ace836 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -777,9 +777,6 @@ <member name="internationalization/rendering/text_driver" type="String" setter="" getter="" default=""""> Specifies the [TextServer] to use. If left empty, the default will be used. </member> - <member name="layer_names/2d_navigation/layer_0" type="String" setter="" getter="" default=""""> - Optional name for the 2D navigation layer 0. If left empty, the layer will display as "Layer 0". - </member> <member name="layer_names/2d_navigation/layer_1" type="String" setter="" getter="" default=""""> Optional name for the 2D navigation layer 1. If left empty, the layer will display as "Layer 1". </member> @@ -855,6 +852,9 @@ <member name="layer_names/2d_navigation/layer_31" type="String" setter="" getter="" default=""""> Optional name for the 2D navigation layer 31. If left empty, the layer will display as "Layer 31". </member> + <member name="layer_names/2d_navigation/layer_32" type="String" setter="" getter="" default=""""> + Optional name for the 2D navigation layer 32. If left empty, the layer will display as "Layer 32". + </member> <member name="layer_names/2d_navigation/layer_4" type="String" setter="" getter="" default=""""> Optional name for the 2D navigation layer 4. If left empty, the layer will display as "Layer 4". </member> @@ -873,9 +873,6 @@ <member name="layer_names/2d_navigation/layer_9" type="String" setter="" getter="" default=""""> Optional name for the 2D navigation layer 9. If left empty, the layer will display as "Layer 9". </member> - <member name="layer_names/2d_physics/layer_0" type="String" setter="" getter="" default=""""> - Optional name for the 2D physics layer 0. If left empty, the layer will display as "Layer 0". - </member> <member name="layer_names/2d_physics/layer_1" type="String" setter="" getter="" default=""""> Optional name for the 2D physics layer 1. If left empty, the layer will display as "Layer 1". </member> @@ -951,6 +948,9 @@ <member name="layer_names/2d_physics/layer_31" type="String" setter="" getter="" default=""""> Optional name for the 2D physics layer 31. If left empty, the layer will display as "Layer 31". </member> + <member name="layer_names/2d_physics/layer_32" type="String" setter="" getter="" default=""""> + Optional name for the 2D physics layer 32. If left empty, the layer will display as "Layer 32". + </member> <member name="layer_names/2d_physics/layer_4" type="String" setter="" getter="" default=""""> Optional name for the 2D physics layer 4. If left empty, the layer will display as "Layer 4". </member> @@ -969,9 +969,6 @@ <member name="layer_names/2d_physics/layer_9" type="String" setter="" getter="" default=""""> Optional name for the 2D physics layer 9. If left empty, the layer will display as "Layer 9". </member> - <member name="layer_names/2d_render/layer_0" type="String" setter="" getter="" default=""""> - Optional name for the 2D render layer 0. If left empty, the layer will display as "Layer 0". - </member> <member name="layer_names/2d_render/layer_1" type="String" setter="" getter="" default=""""> Optional name for the 2D render layer 1. If left empty, the layer will display as "Layer 1". </member> @@ -1008,6 +1005,9 @@ <member name="layer_names/2d_render/layer_2" type="String" setter="" getter="" default=""""> Optional name for the 2D render layer 2. If left empty, the layer will display as "Layer 2". </member> + <member name="layer_names/2d_render/layer_20" type="String" setter="" getter="" default=""""> + Optional name for the 2D render layer 20. If left empty, the layer will display as "Layer 20". + </member> <member name="layer_names/2d_render/layer_3" type="String" setter="" getter="" default=""""> Optional name for the 2D render layer 3. If left empty, the layer will display as "Layer 3". </member> @@ -1029,9 +1029,6 @@ <member name="layer_names/2d_render/layer_9" type="String" setter="" getter="" default=""""> Optional name for the 2D render layer 9. If left empty, the layer will display as "Layer 9". </member> - <member name="layer_names/3d_navigation/layer_0" type="String" setter="" getter="" default=""""> - Optional name for the 3D navigation layer 0. If left empty, the layer will display as "Layer 0". - </member> <member name="layer_names/3d_navigation/layer_1" type="String" setter="" getter="" default=""""> Optional name for the 3D navigation layer 1. If left empty, the layer will display as "Layer 1". </member> @@ -1107,6 +1104,9 @@ <member name="layer_names/3d_navigation/layer_31" type="String" setter="" getter="" default=""""> Optional name for the 3D navigation layer 31. If left empty, the layer will display as "Layer 31". </member> + <member name="layer_names/3d_navigation/layer_32" type="String" setter="" getter="" default=""""> + Optional name for the 3D navigation layer 32. If left empty, the layer will display as "Layer 32". + </member> <member name="layer_names/3d_navigation/layer_4" type="String" setter="" getter="" default=""""> Optional name for the 3D navigation layer 4. If left empty, the layer will display as "Layer 4". </member> @@ -1125,9 +1125,6 @@ <member name="layer_names/3d_navigation/layer_9" type="String" setter="" getter="" default=""""> Optional name for the 3D navigation layer 9. If left empty, the layer will display as "Layer 9". </member> - <member name="layer_names/3d_physics/layer_0" type="String" setter="" getter="" default=""""> - Optional name for the 3D physics layer 0. If left empty, the layer will display as "Layer 0". - </member> <member name="layer_names/3d_physics/layer_1" type="String" setter="" getter="" default=""""> Optional name for the 3D physics layer 1. If left empty, the layer will display as "Layer 1". </member> @@ -1203,6 +1200,9 @@ <member name="layer_names/3d_physics/layer_31" type="String" setter="" getter="" default=""""> Optional name for the 3D physics layer 31. If left empty, the layer will display as "Layer 31". </member> + <member name="layer_names/3d_physics/layer_32" type="String" setter="" getter="" default=""""> + Optional name for the 3D physics layer 32. If left empty, the layer will display as "Layer 32". + </member> <member name="layer_names/3d_physics/layer_4" type="String" setter="" getter="" default=""""> Optional name for the 3D physics layer 4. If left empty, the layer will display as "Layer 4". </member> @@ -1221,9 +1221,6 @@ <member name="layer_names/3d_physics/layer_9" type="String" setter="" getter="" default=""""> Optional name for the 3D physics layer 9. If left empty, the layer will display as "Layer 9". </member> - <member name="layer_names/3d_render/layer_0" type="String" setter="" getter="" default=""""> - Optional name for the 2D render layer 0. If left empty, the layer will display as "Layer 0". - </member> <member name="layer_names/3d_render/layer_1" type="String" setter="" getter="" default=""""> Optional name for the 3D render layer 1. If left empty, the layer will display as "Layer 1". </member> @@ -1260,6 +1257,9 @@ <member name="layer_names/3d_render/layer_2" type="String" setter="" getter="" default=""""> Optional name for the 3D render layer 2. If left empty, the layer will display as "Layer 2". </member> + <member name="layer_names/3d_render/layer_20" type="String" setter="" getter="" default=""""> + Optional name for the 3D render layer 20. If left empty, the layer will display as "Layer 20". + </member> <member name="layer_names/3d_render/layer_3" type="String" setter="" getter="" default=""""> Optional name for the 3D render layer 3. If left empty, the layer will display as "Layer 3". </member> diff --git a/doc/classes/RayCast2D.xml b/doc/classes/RayCast2D.xml index d8a5fc8508..1d32db8078 100644 --- a/doc/classes/RayCast2D.xml +++ b/doc/classes/RayCast2D.xml @@ -53,11 +53,11 @@ Returns the shape ID of the first object that the ray intersects, or [code]0[/code] if no object is intersecting the ray (i.e. [method is_colliding] returns [code]false[/code]). </description> </method> - <method name="get_collision_mask_bit" qualifiers="const"> + <method name="get_collision_mask_value" qualifiers="const"> <return type="bool" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <description> - Returns an individual bit on the collision mask. + Returns whether or not the specified layer of the [member collision_mask] is enabled, given a [code]layer_number[/code] between 1 and 32. </description> </method> <method name="get_collision_normal" qualifiers="const"> @@ -93,12 +93,12 @@ Removes a collision exception so the ray does report collisions with the specified [RID]. </description> </method> - <method name="set_collision_mask_bit"> + <method name="set_collision_mask_value"> <return type="void" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <argument index="1" name="value" type="bool" /> <description> - Sets or clears individual bits on the collision mask. This makes selecting the areas scanned easier. + Based on [code]value[/code], enables or disables the specified layer in the [member collision_mask], given a [code]layer_number[/code] between 1 and 32. </description> </method> </methods> diff --git a/doc/classes/RayCast3D.xml b/doc/classes/RayCast3D.xml index 4e8eb960db..8628ab7dac 100644 --- a/doc/classes/RayCast3D.xml +++ b/doc/classes/RayCast3D.xml @@ -55,12 +55,11 @@ Returns the shape ID of the first object that the ray intersects, or [code]0[/code] if no object is intersecting the ray (i.e. [method is_colliding] returns [code]false[/code]). </description> </method> - <method name="get_collision_mask_bit" qualifiers="const"> + <method name="get_collision_mask_value" qualifiers="const"> <return type="bool" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <description> - Returns [code]true[/code] if the bit index passed is turned on. - [b]Note:[/b] Bit indices range from 0-19. + Returns whether or not the specified layer of the [member collision_mask] is enabled, given a [code]layer_number[/code] between 1 and 32. </description> </method> <method name="get_collision_normal" qualifiers="const"> @@ -96,13 +95,12 @@ Removes a collision exception so the ray does report collisions with the specified [RID]. </description> </method> - <method name="set_collision_mask_bit"> + <method name="set_collision_mask_value"> <return type="void" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <argument index="1" name="value" type="bool" /> <description> - Sets the bit index passed to the [code]value[/code] passed. - [b]Note:[/b] Bit indexes range from 0-19. + Based on [code]value[/code], enables or disables the specified layer in the [member collision_mask], given a [code]layer_number[/code] between 1 and 32. </description> </method> </methods> diff --git a/doc/classes/SoftBody3D.xml b/doc/classes/SoftBody3D.xml index 53bd7e67bf..ddfc14ceac 100644 --- a/doc/classes/SoftBody3D.xml +++ b/doc/classes/SoftBody3D.xml @@ -23,18 +23,18 @@ Returns an array of nodes that were added as collision exceptions for this body. </description> </method> - <method name="get_collision_layer_bit" qualifiers="const"> + <method name="get_collision_layer_value" qualifiers="const"> <return type="bool" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <description> - Returns an individual bit on the collision mask. + Returns whether or not the specified layer of the [member collision_layer] is enabled, given a [code]layer_number[/code] between 1 and 32. </description> </method> - <method name="get_collision_mask_bit" qualifiers="const"> + <method name="get_collision_mask_value" qualifiers="const"> <return type="bool" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <description> - Returns an individual bit on the collision mask. + Returns whether or not the specified layer of the [member collision_mask] is enabled, given a [code]layer_number[/code] between 1 and 32. </description> </method> <method name="get_physics_rid" qualifiers="const"> @@ -49,20 +49,20 @@ Removes a body from the list of bodies that this body can't collide with. </description> </method> - <method name="set_collision_layer_bit"> + <method name="set_collision_layer_value"> <return type="void" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <argument index="1" name="value" type="bool" /> <description> - Sets individual bits on the layer mask. Use this if you only need to change one layer's value. + Based on [code]value[/code], enables or disables the specified layer in the [member collision_layer], given a [code]layer_number[/code] between 1 and 32. </description> </method> - <method name="set_collision_mask_bit"> + <method name="set_collision_mask_value"> <return type="void" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <argument index="1" name="value" type="bool" /> <description> - Sets individual bits on the collision mask. Use this if you only need to change one layer's value. + Based on [code]value[/code], enables or disables the specified layer in the [member collision_mask], given a [code]layer_number[/code] between 1 and 32. </description> </method> </methods> diff --git a/doc/classes/VisualInstance3D.xml b/doc/classes/VisualInstance3D.xml index 2d9c266f3c..f949fbe7c0 100644 --- a/doc/classes/VisualInstance3D.xml +++ b/doc/classes/VisualInstance3D.xml @@ -27,11 +27,11 @@ Returns the RID of this instance. This RID is the same as the RID returned by [method RenderingServer.instance_create]. This RID is needed if you want to call [RenderingServer] functions directly on this [VisualInstance3D]. </description> </method> - <method name="get_layer_mask_bit" qualifiers="const"> + <method name="get_layer_mask_value" qualifiers="const"> <return type="bool" /> - <argument index="0" name="layer" type="int" /> + <argument index="0" name="layer_number" type="int" /> <description> - Returns [code]true[/code] when the specified layer is enabled in [member layers] and [code]false[/code] otherwise. + Returns whether or not the specified layer of the [member layers] is enabled, given a [code]layer_number[/code] between 1 and 20. </description> </method> <method name="get_transformed_aabb" qualifiers="const"> @@ -48,12 +48,12 @@ Sets the resource that is instantiated by this [VisualInstance3D], which changes how the engine handles the [VisualInstance3D] under the hood. Equivalent to [method RenderingServer.instance_set_base]. </description> </method> - <method name="set_layer_mask_bit"> + <method name="set_layer_mask_value"> <return type="void" /> - <argument index="0" name="layer" type="int" /> - <argument index="1" name="enabled" type="bool" /> + <argument index="0" name="layer_number" type="int" /> + <argument index="1" name="value" type="bool" /> <description> - Enables a particular layer in [member layers]. + Based on [code]value[/code], enables or disables the specified layer in the [member layers], given a [code]layer_number[/code] between 1 and 20. </description> </method> </methods> diff --git a/editor/SCsub b/editor/SCsub index a976c4ed12..d149cc6273 100644 --- a/editor/SCsub +++ b/editor/SCsub @@ -30,7 +30,10 @@ if env["tools"]: reg_exporters_inc = '#include "register_exporters.h"\n' reg_exporters = "void register_exporters() {\n" for e in env.platform_exporters: - env.add_source_files(env.editor_sources, "#platform/" + e + "/export/export.cpp") + # Glob all .cpp files in export folder + files = Glob("#platform/" + e + "/export/" + "*.cpp") + env.add_source_files(env.editor_sources, files) + reg_exporters += "\tregister_" + e + "_exporter();\n" reg_exporters_inc += '#include "platform/' + e + '/export/export.h"\n' reg_exporters += "}\n" diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index ecc11998ac..99619cfc40 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -837,7 +837,7 @@ public: Vector2 offset; offset.y = rect2.size.y * 0.75; - draw_string(font, rect2.position + offset, itos(layer_index), HALIGN_CENTER, rect2.size.x, -1, on ? text_color_on : text_color); + draw_string(font, rect2.position + offset, itos(layer_index + 1), HALIGN_CENTER, rect2.size.x, -1, on ? text_color_on : text_color); ofs.x += bsize + 1; @@ -993,12 +993,12 @@ void EditorPropertyLayers::setup(LayerType p_layer_type) { for (int i = 0; i < layer_count; i++) { String name; - if (ProjectSettings::get_singleton()->has_setting(basename + vformat("/layer_%d", i))) { - name = ProjectSettings::get_singleton()->get(basename + vformat("/layer_%d", i)); + if (ProjectSettings::get_singleton()->has_setting(basename + vformat("/layer_%d", i + 1))) { + name = ProjectSettings::get_singleton()->get(basename + vformat("/layer_%d", i + 1)); } if (name == "") { - name = vformat(TTR("Layer %d"), i); + name = vformat(TTR("Layer %d"), i + 1); } names.push_back(name); diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index 37a04a1844..d6ac238414 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -592,6 +592,14 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) { } if (vsnode->is_use_prop_slots()) { + String error = vsnode->get_warning(visual_shader->get_mode(), p_type); + if (error != String()) { + Label *error_label = memnew(Label); + error_label->add_theme_color_override("font_color", VisualShaderEditor::get_singleton()->get_theme_color(SNAME("error_color"), SNAME("Editor"))); + error_label->set_text(error); + node->add_child(error_label); + } + return; } custom_editor = nullptr; @@ -2029,6 +2037,8 @@ void VisualShaderEditor::_uniform_line_edit_changed(const String &p_text, int p_ undo_redo->add_undo_method(node.ptr(), "set_uniform_name", node->get_uniform_name()); undo_redo->add_do_method(graph_plugin.ptr(), "set_uniform_name", type, p_node_id, validated_name); undo_redo->add_undo_method(graph_plugin.ptr(), "set_uniform_name", type, p_node_id, node->get_uniform_name()); + undo_redo->add_do_method(graph_plugin.ptr(), "update_node_deferred", type, p_node_id); + undo_redo->add_undo_method(graph_plugin.ptr(), "update_node_deferred", type, p_node_id); undo_redo->add_do_method(this, "_update_uniforms", true); undo_redo->add_undo_method(this, "_update_uniforms", true); diff --git a/editor/translations/af.po b/editor/translations/af.po index 18ad28af62..70e016ee65 100644 --- a/editor/translations/af.po +++ b/editor/translations/af.po @@ -353,6 +353,7 @@ msgstr "Verander Anim Herspeel" msgid "Remove Anim Track" msgstr "Verwyder Anim Baan" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Skep NUWE baan vir %s en voeg sleutel by?" @@ -377,10 +378,27 @@ msgstr "Skep" msgid "Anim Insert" msgstr "Anim Voeg In" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animasie Zoem." + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "Eienskappe" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Anim Skep & Voeg by" @@ -975,7 +993,7 @@ msgstr "Skep Nuwe" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2363,6 +2381,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -3157,10 +3186,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp #, fuzzy msgid "Update Continuously" msgstr "Deurlopend" @@ -3782,6 +3807,15 @@ msgid "Download from:" msgstr "" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Open 'n Lêer" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8694,6 +8728,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Hernoem AutoLaai" @@ -8724,6 +8764,12 @@ msgid "Remove All StyleBox Items" msgstr "Hernoem AutoLaai" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Gunstelinge:" @@ -12290,6 +12336,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13942,6 +13996,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/ar.po b/editor/translations/ar.po index 9093792ef8..eb11aa27b6 100644 --- a/editor/translations/ar.po +++ b/editor/translations/ar.po @@ -389,6 +389,7 @@ msgstr "تغيير وضع عقدة Ø§Ù„ØØ±ÙƒØ©" msgid "Remove Anim Track" msgstr "ØØ°Ù مسار Ø§Ù„ØªØØ±ÙŠÙƒ" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "أنشئ مسار جديد Ù„ %s Ùˆ إدخال Ù…ÙØªØ§ØØŸ" @@ -413,10 +414,28 @@ msgstr "أنشئ" msgid "Anim Insert" msgstr "إدخال ØØ±ÙƒØ©" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "لا يمكن ÙØªØ '%s'." + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "رسوم Ù…ØªØØ±ÙƒØ©" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "اللأعب Ø§Ù„Ù…ØªØØ±Ùƒ لا يستطيع ØªØØ±ÙŠÙƒ Ù†ÙØ³Ù‡ ,Ùقط اللاعبين الآخرين." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "لا خاصية '%s' موجودة." + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "أنشي ØØ±ÙƒØ© وأدخلها" @@ -988,7 +1007,7 @@ msgstr "إنشاء %s جديد" msgid "No results for \"%s\"." msgstr "لا نتائج من أجل \"%s\"." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2333,6 +2352,17 @@ msgid "New Window" msgstr "Ù†Ø§ÙØ°Ø© جديدة" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "قم بالتدوير أثناء إعادة رسم Ù†Ø§ÙØ°Ø© Ø§Ù„Ù…ØØ±Ù‘ر." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "لا يمكن ØÙظ الموارد المستوردة." @@ -3187,10 +3217,6 @@ msgid "Save & Restart" msgstr "ØÙظ Ùˆ إعادة تشغيل" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "قم بالتدوير أثناء إعادة رسم Ù†Ø§ÙØ°Ø© Ø§Ù„Ù…ØØ±Ù‘ر." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "ØªØØ¯ÙŠØ« متواصل" @@ -3851,6 +3877,16 @@ msgid "Download from:" msgstr "خطأ ÙÙŠ التØÙ…يل" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "تشغيل ÙÙŠ Ø§Ù„Ù…ØªØµÙØ" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "خطأ ÙÙŠ نسخ" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8708,6 +8744,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "إزالة جميع العناصر" @@ -8738,6 +8780,12 @@ msgid "Remove All StyleBox Items" msgstr "إزالة جميع العناصر" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Ø¥Ø¶Ø§ÙØ© بنود للصنÙ" @@ -12401,6 +12449,16 @@ msgstr "تعديل Ø§Ø±ØªÙØ§Ø¹ الشكل الأسطواني" msgid "Change Ray Shape Length" msgstr "تعديل طول الشكل الشعاعي" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "ØØ¯Ø¯ موقع نقطة الإنØÙ†Ø§Ø¡" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "ØØ¯Ø¯ موقع نقطة الإنØÙ†Ø§Ø¡" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "تغيير نص٠قطر الاسطوانة" @@ -14183,6 +14241,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "سيتم تجاهل هذا الجسم ØØªÙ‰ تضع ØªØØ¯Ø¯ له مجسمًا." @@ -15136,9 +15230,6 @@ msgstr "لا يمكن تعديل الثوابت." #~ msgid "I see..." #~ msgstr "أنا أري..." -#~ msgid "Can't open '%s'." -#~ msgstr "لا يمكن ÙØªØ '%s'." - #~ msgid "Ugh" #~ msgstr "آخخ" diff --git a/editor/translations/az.po b/editor/translations/az.po index 4ac0ae6469..6c07f98d38 100644 --- a/editor/translations/az.po +++ b/editor/translations/az.po @@ -358,6 +358,7 @@ msgstr "Animasya Döngü Rejimini DÉ™yiÅŸ" msgid "Remove Anim Track" msgstr "Animasya İzini Sil" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "%s üçün YENİ iz yaradılsın vÉ™ açar daxil edilsin?" @@ -383,11 +384,27 @@ msgstr "Yarat" msgid "Anim Insert" msgstr "Animasiya Daxil Et" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animasiyanı TÉ™mizlÉ™mÉ™" + #: editor/animation_track_editor.cpp #, fuzzy msgid "AnimationPlayer can't animate itself, only other players." msgstr "AnimationPlayer özünü canlandıra bilmÉ™z, yalnız digÉ™r playerlÉ™r." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "property '%s'" +msgstr "" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Animasiya yaradın vÉ™ É™lavÉ™ edin" @@ -975,7 +992,7 @@ msgstr "Yeni %s yarat" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2286,6 +2303,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -3065,10 +3093,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "" @@ -3676,6 +3700,14 @@ msgid "Download from:" msgstr "" #: editor/export_template_manager.cpp +msgid "Open in Web Browser" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8367,6 +8399,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Remove All Color Items" msgstr "" @@ -8391,6 +8429,12 @@ msgid "Remove All StyleBox Items" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Add Color Item" msgstr "" @@ -11802,6 +11846,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13399,6 +13451,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/bg.po b/editor/translations/bg.po index b0bf9a4d6c..3045c7b781 100644 --- a/editor/translations/bg.po +++ b/editor/translations/bg.po @@ -350,6 +350,7 @@ msgstr "ПромÑна на режима на повтарÑне на анима msgid "Remove Anim Track" msgstr "" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Създаване на ÐОВРпътечка за %s и вмъкване на ключ?" @@ -374,10 +375,28 @@ msgstr "Създаване" msgid "Anim Insert" msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "Избиране на вÑичко" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "ÐнимациÑ" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "СвойÑтво" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "" @@ -934,7 +953,7 @@ msgstr "Създаване на %s" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2244,6 +2263,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -3050,10 +3080,6 @@ msgid "Save & Restart" msgstr "Запазване и реÑтартиране" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "" @@ -3677,6 +3703,16 @@ msgid "Download from:" msgstr "Грешка при ÑвалÑнето" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "ОтварÑне във Ñ„Ð°Ð¹Ð»Ð¾Ð²Ð¸Ñ Ð¼ÐµÐ½Ð¸Ð´Ð¶ÑŠÑ€" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "Копиране на грешката" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8462,6 +8498,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Премахване на вÑички елементи" @@ -8492,6 +8534,12 @@ msgid "Remove All StyleBox Items" msgstr "Премахване на вÑички елементи" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "ДобавÑне на вÑички елементи" @@ -11965,6 +12013,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13637,6 +13693,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "Това Ñ‚Ñло ще бъде игнорирано, докато не зададете полигонна мрежа." @@ -14016,10 +14108,6 @@ msgstr "КонÑтантите не могат да бъдат промененР#~ msgid "Select Mode (Q)" #~ msgstr "Режим на Селектиране (Q)" -#, fuzzy -#~ msgid "Snap Mode (%s)" -#~ msgstr "Избиране на вÑичко" - #~ msgid "Project List" #~ msgstr "СпиÑък Ñ Ð¿Ñ€Ð¾ÐµÐºÑ‚Ð¸" diff --git a/editor/translations/bn.po b/editor/translations/bn.po index 384ea57f6c..6cd9e3a81c 100644 --- a/editor/translations/bn.po +++ b/editor/translations/bn.po @@ -345,6 +345,7 @@ msgstr "অà§à¦¯à¦¾à¦¨à¦¿à¦®à§‡à¦¶à¦¨à§‡à¦° লà§à¦ª মোড পরিবর msgid "Remove Anim Track" msgstr "অà§à¦¯à¦¾à¦¨à¦¿à¦®à§‡à¦¶à¦¨ (Anim) টà§à¦°à§à¦¯à¦¾à¦• রিমà§à¦ করà§à¦¨" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "%s à¦à¦° জনà§à¦¯ নতà§à¦¨ টà§à¦°à§à¦¯à¦¾à¦•/পথ তৈরি করতে à¦à¦¬à¦‚ চাবি পà§à¦°à¦¬à§‡à¦¶ করাতে চান?" @@ -369,10 +370,28 @@ msgstr "তৈরি করà§à¦¨" msgid "Anim Insert" msgstr "অà§à¦¯à¦¾à¦¨à¦¿à¦®à§‡à¦¶à¦¨à§‡ (Anim) অনà§à¦¤à¦°à§à¦à§à¦•à§à¦¤ করà§à¦¨" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "'..' তে পরিচালনা করা সমà§à¦à¦¬ নয়" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "অà§à¦¯à¦¾à¦¨à¦¿à¦®à§‡à¦¶à¦¨" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "অà§à¦¯à¦¾à¦¨à¦¿à¦®à§‡à¦¶à¦¨à¦ªà§à¦²à§‡à¦¯à¦¼à¦¾à¦° নিজেই অà§à¦¯à¦¾à¦¨à¦¿à¦®à§‡à¦Ÿ করতে পারে না, কেবল অনà§à¦¯ পà§à¦²à§‡à§Ÿà¦¾à¦°à¥¤" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "পà§à¦°à¦ªà¦¾à¦°à§à¦Ÿà¦¿:" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "অà§à¦¯à¦¾à¦¨à¦¿à¦®à§‡à¦¶à¦¨ (Anim) তৈরি à¦à¦¬à¦‚ যোগ করà§à¦¨" @@ -969,7 +988,7 @@ msgstr "নতà§à¦¨ তৈরি করà§à¦¨" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2392,6 +2411,18 @@ msgid "New Window" msgstr "উইনà§à¦¡à§‹" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +#, fuzzy +msgid "Spins when the editor window redraws." +msgstr "à¦à¦¡à¦¿à¦Ÿà¦°à§‡à¦° পà§à¦¨-অঙà§à¦•নে à¦à¦Ÿà¦¿ ঘূরà§à¦£à¦¨ করে!" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -3286,11 +3317,6 @@ msgstr "সংরকà§à¦·à¦£ à¦à¦¬à¦‚ পà§à¦¨-ইমà§à¦ªà§‹à¦°à§à¦Ÿ কঠ#: editor/editor_node.cpp #, fuzzy -msgid "Spins when the editor window redraws." -msgstr "à¦à¦¡à¦¿à¦Ÿà¦°à§‡à¦° পà§à¦¨-অঙà§à¦•নে à¦à¦Ÿà¦¿ ঘূরà§à¦£à¦¨ করে!" - -#: editor/editor_node.cpp -#, fuzzy msgid "Update Continuously" msgstr "অবিচà§à¦›à¦¿à¦¨à§à¦¨/নিরবচà§à¦›à¦¿à¦¨à§à¦¨" @@ -3979,6 +4005,16 @@ msgid "Download from:" msgstr "নীচে" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "বà§à¦°à¦¾à¦‰à¦¸" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "à¦à§à¦²/সমসà§à¦¯à¦¾-সমূহ লোড করà§à¦¨" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -9179,6 +9215,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "কà§à¦²à¦¾à¦¸à§‡à¦° আইটেম অপসারণ করà§à¦¨" @@ -9209,6 +9251,12 @@ msgid "Remove All StyleBox Items" msgstr "কà§à¦²à¦¾à¦¸à§‡à¦° আইটেম অপসারণ করà§à¦¨" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "কà§à¦²à¦¾à¦¸à§‡à¦° আইটেম যোগ করà§à¦¨" @@ -13015,6 +13063,16 @@ msgstr "Capsule Shape à¦à¦° উচà§à¦šà¦¤à¦¾ পরিবরà§à¦¤à¦¨ করà msgid "Change Ray Shape Length" msgstr "Ray Shape à¦à¦° দৈরà§à¦˜à§à¦¯ পরিবরà§à¦¤à¦¨ করà§à¦¨" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "বকà§à¦°à¦°à§‡à¦–ার বিনà§à¦¦à§à¦° সà§à¦¥à¦¾à¦¨ নিরà§à¦§à¦¾à¦°à¦£ করà§à¦¨" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "বকà§à¦°à¦°à§‡à¦–ার বিনà§à¦¦à§à¦° সà§à¦¥à¦¾à¦¨ নিরà§à¦§à¦¾à¦°à¦£ করà§à¦¨" + #: modules/csg/csg_gizmos.cpp #, fuzzy msgid "Change Cylinder Radius" @@ -14801,6 +14859,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" @@ -16018,10 +16112,6 @@ msgstr "" #~ msgid "I see..." #~ msgstr "বà§à¦à¦²à¦¾à¦®..." -#, fuzzy -#~ msgid "Can't open '%s'." -#~ msgstr "'..' তে পরিচালনা করা সমà§à¦à¦¬ নয়" - #~ msgid "Ugh" #~ msgstr "আহà§â€Œ" diff --git a/editor/translations/br.po b/editor/translations/br.po index 0f61544190..adee6daaba 100644 --- a/editor/translations/br.po +++ b/editor/translations/br.po @@ -340,6 +340,7 @@ msgstr "Cheñch Mod Treiñ ar Fiñvskeudenn" msgid "Remove Anim Track" msgstr "Dilemel ar Roudenn Fiñvskeudenn" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Krouiñ ur roudenn NEVEZ evit %s ha enlakaat an alc'hwez ?" @@ -364,12 +365,28 @@ msgstr "Krouiñ" msgid "Anim Insert" msgstr "Enlakaat Fiñvskeudenn" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Tro Fiñvskeudenn" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" "AnimationPlayer n'hall ket en em lakaat de fiñval, met nemet al lennerezhioù " "all." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "property '%s'" +msgstr "" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Krouiñ & Enlakaat Fiñvskeudenn" @@ -934,7 +951,7 @@ msgstr "" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2226,6 +2243,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -3005,10 +3033,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "" @@ -3614,6 +3638,14 @@ msgid "Download from:" msgstr "" #: editor/export_template_manager.cpp +msgid "Open in Web Browser" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8294,6 +8326,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Remove All Color Items" msgstr "" @@ -8318,6 +8356,12 @@ msgid "Remove All StyleBox Items" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Add Color Item" msgstr "" @@ -11725,6 +11769,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13320,6 +13372,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/ca.po b/editor/translations/ca.po index ab96816ec0..347fea679b 100644 --- a/editor/translations/ca.po +++ b/editor/translations/ca.po @@ -350,6 +350,7 @@ msgstr "Canviar Mode de bucle d'Animació" msgid "Remove Anim Track" msgstr "Treu la Pista" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Voleu crear una NOVA pista per a %s i inserir-hi una clau?" @@ -374,11 +375,29 @@ msgstr "Crea" msgid "Anim Insert" msgstr "Insereix una Animació" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "No es pot obrir '%s' ." + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animació" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" "Un AnimationPlayer no pot animar-se a si mateix, només altres reproductors." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "No existeix cap propietat '%s'." + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Crea i Insereix" @@ -958,7 +977,7 @@ msgstr "Crea Nou %s" msgid "No results for \"%s\"." msgstr "No hi ha cap resultat per a «%s»." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2315,6 +2334,17 @@ msgid "New Window" msgstr "Finestra nova" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "Gira quan la finestra de l'editor es redibuixa." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Els recursos importats no es poden desar." @@ -3190,10 +3220,6 @@ msgid "Save & Restart" msgstr "Desa i Reinicia" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "Gira quan la finestra de l'editor es redibuixa." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "Actualitzar contÃnuament" @@ -3868,6 +3894,16 @@ msgid "Download from:" msgstr "Error en la Baixada" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Executa-ho en el Navegador" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "Copia l'error" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8863,6 +8899,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Treu tots els Elements" @@ -8893,6 +8935,12 @@ msgid "Remove All StyleBox Items" msgstr "Treu tots els Elements" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Afegeix Elements de Classe" @@ -12713,6 +12761,16 @@ msgstr "Modifica l'alçada de la Forma Caixa" msgid "Change Ray Shape Length" msgstr "Modifica la longitud de la Forma Raig" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "Estableix la Posició del Punt de la Corba" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "Estableix la Posició del Punt de la Corba" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "Canviar Radi del Cilindre" @@ -14528,6 +14586,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp #, fuzzy msgid "This body will be ignored until you set a mesh." @@ -15820,9 +15914,6 @@ msgstr "Les constants no es poden modificar." #~ msgid "I see..." #~ msgstr "Vaja..." -#~ msgid "Can't open '%s'." -#~ msgstr "No es pot obrir '%s' ." - #~ msgid "Ugh" #~ msgstr "Uf..." diff --git a/editor/translations/cs.po b/editor/translations/cs.po index 579289300b..266614bf96 100644 --- a/editor/translations/cs.po +++ b/editor/translations/cs.po @@ -362,6 +362,7 @@ msgstr "ZmÄ›nit mód smyÄky animace" msgid "Remove Anim Track" msgstr "Odstranit stopu animace" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "VytvoÅ™it NOVOU stopu pro %s a vložit klÃÄ?" @@ -386,10 +387,28 @@ msgstr "VytvoÅ™it" msgid "Anim Insert" msgstr "Animace: vložit" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "Nelze otevÅ™Ãt '%s'." + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animace" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "AnimationPlayer nemůže animovat sám sebe, pouze ostatnà pÅ™ehrávaÄe." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "Vlastnost '%s' neexistuje." + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Animace: VytvoÅ™it a vložit" @@ -966,7 +985,7 @@ msgstr "VytvoÅ™it nový %s" msgid "No results for \"%s\"." msgstr "Žádné výsledky pro \"%s\"." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2313,6 +2332,17 @@ msgid "New Window" msgstr "Nové okno" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "ToÄà se, když se okno editoru pÅ™ekresluje." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Nelze uložit importované zdroje." @@ -3165,10 +3195,6 @@ msgid "Save & Restart" msgstr "Uložit a restartovat" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "ToÄà se, když se okno editoru pÅ™ekresluje." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "Aktualizovat průběžnÄ›" @@ -3831,6 +3857,16 @@ msgid "Download from:" msgstr "Chyba pÅ™i stahovánÃ" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Spustit v prohlÞeÄi" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "KopÃrovat chybu" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8679,6 +8715,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Odstranit vÅ¡echny položky" @@ -8709,6 +8751,12 @@ msgid "Remove All StyleBox Items" msgstr "Odstranit vÅ¡echny položky" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "PÅ™idat položky tÅ™Ãdy" @@ -12353,6 +12401,16 @@ msgstr "ZmÄ›nit výšku Cylinder Shape" msgid "Change Ray Shape Length" msgstr "ZmÄ›nit délku Ray Shape" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "Nastavit pozici bodu kÅ™ivky" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "Nastavit pozici bodu kÅ™ivky" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "ZmÄ›nit polomÄ›r Cylinder" @@ -14097,6 +14155,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "Toto tÄ›lo bude ignorováno dokud nenastavÃte sÃÅ¥." @@ -15196,9 +15290,6 @@ msgstr "Konstanty nenà možné upravovat." #~ msgid "I see..." #~ msgstr "Chápu..." -#~ msgid "Can't open '%s'." -#~ msgstr "Nelze otevÅ™Ãt '%s'." - #~ msgid "Ugh" #~ msgstr "Ups" diff --git a/editor/translations/da.po b/editor/translations/da.po index 163eb546a0..2ab69b5f05 100644 --- a/editor/translations/da.po +++ b/editor/translations/da.po @@ -363,6 +363,7 @@ msgstr "Ændre Anim Løkke" msgid "Remove Anim Track" msgstr "Fjern Anim Spor" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Opret NYT spor til %s og indsæt nøgle?" @@ -387,10 +388,28 @@ msgstr "Opret" msgid "Anim Insert" msgstr "Anim Indsæt" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "Kan ikke Ã¥bne '%s'." + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animation" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "Animationsafspiller kan ikke animere sig selv, kun andre afspillere." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "Egenskaber" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Anim Opret & Indsæt" @@ -995,7 +1014,7 @@ msgstr "Opret Ny %s" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2393,6 +2412,18 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +#, fuzzy +msgid "Spins when the editor window redraws." +msgstr "Snurrer nÃ¥r editor vinduer gentegnes!" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -3263,11 +3294,6 @@ msgstr "Gem & genstart" #: editor/editor_node.cpp #, fuzzy -msgid "Spins when the editor window redraws." -msgstr "Snurrer nÃ¥r editor vinduer gentegnes!" - -#: editor/editor_node.cpp -#, fuzzy msgid "Update Continuously" msgstr "Kontinuerlig" @@ -3914,6 +3940,16 @@ msgid "Download from:" msgstr "Download" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Ã…bn i FilhÃ¥ndtering" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "Indlæs Fejl" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8912,6 +8948,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Fjern Alt" @@ -8942,6 +8984,12 @@ msgid "Remove All StyleBox Items" msgstr "Fjern Alt" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Favoritter:" @@ -12597,6 +12645,16 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "Fjern Kurve Punktets Position" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "Fjern Kurve Punktets Position" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -14331,6 +14389,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" @@ -15089,9 +15183,6 @@ msgstr "Konstanter kan ikke ændres." #~ msgid "I see..." #~ msgstr "Jeg forstÃ¥r..." -#~ msgid "Can't open '%s'." -#~ msgstr "Kan ikke Ã¥bne '%s'." - #, fuzzy #~ msgid "Ugh" #~ msgstr "Ugh" diff --git a/editor/translations/de.po b/editor/translations/de.po index 38d565b1cd..6d57f3dcad 100644 --- a/editor/translations/de.po +++ b/editor/translations/de.po @@ -75,7 +75,7 @@ msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2021-07-29 02:33+0000\n" +"PO-Revision-Date: 2021-08-06 06:47+0000\n" "Last-Translator: So Wieso <sowieso@dukun.de>\n" "Language-Team: German <https://hosted.weblate.org/projects/godot-engine/" "godot/de/>\n" @@ -84,7 +84,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.7.2-dev\n" +"X-Generator: Weblate 4.8-dev\n" #: core/math/expression.cpp modules/gdscript/gdscript_functions.cpp #: modules/visual_script/visual_script_builtin_funcs.cpp @@ -406,6 +406,7 @@ msgstr "Animationswiederholungsmodus ändern" msgid "Remove Anim Track" msgstr "Spur entfernen" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "NEUE Spur für %s erstellen und Schlüsselbild hinzufügen?" @@ -430,10 +431,28 @@ msgstr "Erstellen" msgid "Anim Insert" msgstr "Einfügen" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "‚%s‘ kann nicht geöffnet werden." + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animation" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "AnimationPlayer kann sich nicht selbst animieren, nur andere Objekte." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "Eigenschaft ‚%s‘ existiert nicht." + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Animation Erstellen & Einfügen" @@ -646,9 +665,8 @@ msgid "Go to Previous Step" msgstr "Zum vorherigen Schritt" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Apply Reset" -msgstr "Zurücksetzen" +msgstr "Zurücksetzen durchführen" #: editor/animation_track_editor.cpp msgid "Optimize Animation" @@ -667,9 +685,8 @@ msgid "Use Bezier Curves" msgstr "Bezier-Kurven nutzen" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Create RESET Track(s)" -msgstr "Spuren einfügen" +msgstr "RESET Spur(en) erstellen" #: editor/animation_track_editor.cpp msgid "Anim. Optimizer" @@ -994,7 +1011,6 @@ msgid "Edit..." msgstr "Bearbeiten..." #: editor/connections_dialog.cpp -#, fuzzy msgid "Go to Method" msgstr "Zur Methode springen" @@ -1014,9 +1030,9 @@ msgstr "%s erstellen" msgid "No results for \"%s\"." msgstr "Keine Ergebnisse für \"%s\"." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." -msgstr "" +msgstr "Keine Beschreibung zu ‚%s‘ verfügbar." #: editor/create_dialog.cpp editor/editor_file_dialog.cpp #: editor/filesystem_dock.cpp @@ -1116,7 +1132,6 @@ msgid "Owners Of:" msgstr "Besitzer von:" #: editor/dependency_editor.cpp -#, fuzzy msgid "" "Remove the selected files from the project? (Cannot be undone.)\n" "Depending on your filesystem configuration, the files will either be moved " @@ -1124,11 +1139,10 @@ msgid "" msgstr "" "Ausgewählte Dateien aus dem Projekt entfernen? (Kann nicht rückgängig " "gemacht werden.)\n" -"Die Dateien können möglicherweise aus dem Papierkorb des Betriebssystems " -"wiederhergestellt werden." +"Abhängig von den Betriebssystemeinstellungen werden die Dateien in den " +"Papierkorb verschoben oder permanent gelöscht." #: editor/dependency_editor.cpp -#, fuzzy msgid "" "The files being removed are required by other resources in order for them to " "work.\n" @@ -1139,8 +1153,8 @@ msgstr "" "Andere Ressourcen benötigen die zu entfernenden Dateien, um richtig zu " "funktionieren.\n" "Trotzdem entfernen? (Kann nicht rückgängig gemacht werden.)\n" -"Die Dateien können möglicherweise aus dem Papierkorb des Betriebssystems " -"wiederhergestellt werden." +"Abhängig von den Betriebssystemeinstellungen werden die Dateien in den " +"Papierkorb verschoben oder permanent gelöscht." #: editor/dependency_editor.cpp msgid "Cannot remove:" @@ -1312,41 +1326,39 @@ msgid "Licenses" msgstr "Lizenzen" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Error opening asset file for \"%s\" (not in ZIP format)." -msgstr "Fehler beim Öffnen der Paketdatei (kein ZIP-Format)." +msgstr "Fehler beim Öffnen der Nutzerinhaltsdatei „%s“ (kein ZIP-Format)." #: editor/editor_asset_installer.cpp -#, fuzzy msgid "%s (already exists)" msgstr "%s (existiert bereits)" #: editor/editor_asset_installer.cpp msgid "Contents of asset \"%s\" - %d file(s) conflict with your project:" msgstr "" +"Nutzerinhalt „%s“ - %d Datei(en) stehen in Konflikt mit diesem Projekt:" #: editor/editor_asset_installer.cpp msgid "Contents of asset \"%s\" - No files conflict with your project:" -msgstr "" +msgstr "Nutzerinhalt „%s“ - Kein Konflikt mit diesem Projekt:" #: editor/editor_asset_installer.cpp msgid "Uncompressing Assets" msgstr "Inhalte werden entpackt" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "The following files failed extraction from asset \"%s\":" -msgstr "Die folgenden Dateien ließen sich nicht aus dem Paket extrahieren:" +msgstr "" +"Die folgenden Dateien ließen sich nicht aus dem Nutzerinhaltspaket „%s“ " +"extrahieren:" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "(and %s more files)" -msgstr "Und %s weitere Dateien." +msgstr "(und %s weitere Dateien)" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Asset \"%s\" installed successfully!" -msgstr "Paket wurde erfolgreich installiert!" +msgstr "Nutzerinhaltspaket „%s“ wurde erfolgreich installiert!" #: editor/editor_asset_installer.cpp #: editor/plugins/asset_library_editor_plugin.cpp @@ -1358,9 +1370,8 @@ msgid "Install" msgstr "Installieren" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Asset Installer" -msgstr "Erweiterungenverwaltung" +msgstr "Nutzerinhalteinstallation" #: editor/editor_audio_buses.cpp msgid "Speakers" @@ -1423,7 +1434,6 @@ msgid "Bypass" msgstr "Überbrückung" #: editor/editor_audio_buses.cpp -#, fuzzy msgid "Bus Options" msgstr "Audiobusoptionen" @@ -1591,13 +1601,12 @@ msgid "Can't add autoload:" msgstr "Autoload konnte nicht hinzugefügt werden:" #: editor/editor_autoload_settings.cpp -#, fuzzy msgid "%s is an invalid path. File does not exist." -msgstr "Datei existiert nicht." +msgstr "%s ist ein ungültiger Pfad. Datei existiert nicht." #: editor/editor_autoload_settings.cpp msgid "%s is an invalid path. Not in resource path (res://)." -msgstr "" +msgstr "%s ist ein ungültiger Pfad. Liegt nicht im Ressourcen-Pfad (res://)." #: editor/editor_autoload_settings.cpp msgid "Add AutoLoad" @@ -1621,9 +1630,8 @@ msgid "Name" msgstr "Name" #: editor/editor_autoload_settings.cpp -#, fuzzy msgid "Global Variable" -msgstr "Variable" +msgstr "Globale Variable" #: editor/editor_data.cpp msgid "Paste Params" @@ -1799,48 +1807,56 @@ msgstr "Importleiste" #: editor/editor_feature_profile.cpp msgid "Allows to view and edit 3D scenes." -msgstr "" +msgstr "Ermöglicht das Betrachten und Bearbeiten von 3D-Szenen." #: editor/editor_feature_profile.cpp msgid "Allows to edit scripts using the integrated script editor." msgstr "" +"Ermöglicht das Bearbeiten von Skripten mithilfe des integrierten Skript-" +"Editors." #: editor/editor_feature_profile.cpp msgid "Provides built-in access to the Asset Library." -msgstr "" +msgstr "Stellt Zugriff zur Nutzerinhaltebibliothek her." #: editor/editor_feature_profile.cpp msgid "Allows editing the node hierarchy in the Scene dock." -msgstr "" +msgstr "Ermöglicht das Bearbeiten der Node-Hierachie in der Szenenleiste." #: editor/editor_feature_profile.cpp msgid "" "Allows to work with signals and groups of the node selected in the Scene " "dock." msgstr "" +"Ermöglicht die Konfiguration von Signalen und Gruppen des ausgewählten Nodes " +"in der Szenenleiste." #: editor/editor_feature_profile.cpp msgid "Allows to browse the local file system via a dedicated dock." msgstr "" +"Ermöglicht das Betrachten des lokalen Dateisystems in einer eigenen Leiste." #: editor/editor_feature_profile.cpp msgid "" "Allows to configure import settings for individual assets. Requires the " "FileSystem dock to function." msgstr "" +"Ermöglicht die Konfiguration von Importeinstellungen für individuelle " +"Dateien. Benötigt die Dateisystemleiste zum Funktionieren." #: editor/editor_feature_profile.cpp -#, fuzzy msgid "(current)" -msgstr "(Aktuell)" +msgstr "(ausgewählt)" #: editor/editor_feature_profile.cpp msgid "(none)" -msgstr "" +msgstr "(keins)" #: editor/editor_feature_profile.cpp msgid "Remove currently selected profile, '%s'? Cannot be undone." msgstr "" +"Aktuell ausgewähltes Profil ‚%s‘ entfernen? Dies kann nicht rückgängig " +"gemacht werden." #: editor/editor_feature_profile.cpp msgid "Profile must be a valid filename and must not contain '.'" @@ -1872,19 +1888,16 @@ msgid "Enable Contextual Editor" msgstr "Kontextsensitiven Editor aktivieren" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Class Properties:" -msgstr "Eigenschaften:" +msgstr "Klasseneigenschaften:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Main Features:" -msgstr "Eigenschaften und Merkmale" +msgstr "Wichtigste Funktionen:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Nodes and Classes:" -msgstr "Aktivierte Klassen:" +msgstr "Nodes und Klassen:" #: editor/editor_feature_profile.cpp msgid "File '%s' format is invalid, import aborted." @@ -1903,7 +1916,6 @@ msgid "Error saving profile to path: '%s'." msgstr "Fehler beim Speichern des Profils im Pfad: ‚%s‘." #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Reset to Default" msgstr "Auf Standardwerte zurücksetzen" @@ -1912,14 +1924,12 @@ msgid "Current Profile:" msgstr "Aktuelles Profil:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Create Profile" -msgstr "Profil löschen" +msgstr "Profil erstellen" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Remove Profile" -msgstr "Kachel entfernen" +msgstr "Profil entfernen" #: editor/editor_feature_profile.cpp msgid "Available Profiles:" @@ -1939,18 +1949,18 @@ msgid "Export" msgstr "Exportieren" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Configure Selected Profile:" -msgstr "Aktuelles Profil:" +msgstr "Ausgewähltes Profil bearbeiten:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Extra Options:" -msgstr "Textureinstellungen" +msgstr "Zusatzoptionen:" #: editor/editor_feature_profile.cpp msgid "Create or import a profile to edit available classes and properties." msgstr "" +"Ein Profil erstellen oder importieren um verfügbare Klassen und " +"Einstellungen zu bearbeiten." #: editor/editor_feature_profile.cpp msgid "New profile name:" @@ -1977,9 +1987,8 @@ msgid "Select Current Folder" msgstr "Gegenwärtigen Ordner auswählen" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp -#, fuzzy msgid "File exists, overwrite?" -msgstr "Datei existiert bereits. Überschreiben?" +msgstr "Datei existiert bereits. Soll sie überschrieben werden?" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp msgid "Select This Folder" @@ -2372,6 +2381,17 @@ msgid "New Window" msgstr "Neues Fenster" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "Dreht sich, wenn das Editorfenster neu gezeichnet wird." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Importierte Ressourcen können nicht abgespeichert werden." @@ -2604,13 +2624,16 @@ msgid "" "The current scene has no root node, but %d modified external resource(s) " "were saved anyway." msgstr "" +"Die aktuelle Szene hat keine Wurzel, dennoch wurden %d bearbeitete externe " +"Ressource(n) gespeichert." #: editor/editor_node.cpp -#, fuzzy msgid "" "A root node is required to save the scene. You can add a root node using the " "Scene tree dock." -msgstr "Ein Wurzel-Node wird benötigt um diese Szene zu speichern." +msgstr "" +"Ein Wurzel-Node wird benötigt um diese Szene zu speichern. Ein Wurzel-Node " +"kann in der Szenenbaumleiste hinzugefügt werden." #: editor/editor_node.cpp msgid "Save Scene As..." @@ -3004,9 +3027,8 @@ msgid "Orphan Resource Explorer..." msgstr "Verwaltung nicht verwendeter Ressourcen…" #: editor/editor_node.cpp -#, fuzzy msgid "Reload Current Project" -msgstr "Projekt umbenennen" +msgstr "Aktuelles Projekt neu laden" #: editor/editor_node.cpp msgid "Quit to Project List" @@ -3167,22 +3189,20 @@ msgid "Help" msgstr "Hilfe" #: editor/editor_node.cpp -#, fuzzy msgid "Online Documentation" -msgstr "Dokumentation öffnen" +msgstr "Internet-Dokumentation" #: editor/editor_node.cpp msgid "Questions & Answers" -msgstr "" +msgstr "Fragen & Antworten" #: editor/editor_node.cpp msgid "Report a Bug" msgstr "Fehler berichten" #: editor/editor_node.cpp -#, fuzzy msgid "Suggest a Feature" -msgstr "Einen Wert setzen" +msgstr "Neue Funktionalität vorschlagen" #: editor/editor_node.cpp msgid "Send Docs Feedback" @@ -3190,12 +3210,11 @@ msgstr "Dokumentationsvorschläge senden" #: editor/editor_node.cpp editor/plugins/asset_library_editor_plugin.cpp msgid "Community" -msgstr "Community" +msgstr "Gemeinschaft" #: editor/editor_node.cpp -#, fuzzy msgid "About Godot" -msgstr "Über" +msgstr "Über Godot" #: editor/editor_node.cpp msgid "Support Godot Development" @@ -3247,10 +3266,6 @@ msgid "Save & Restart" msgstr "Speichern & Neu starten" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "Dreht sich, wenn das Editorfenster neu gezeichnet wird." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "Fortlaufend aktualisieren" @@ -3292,14 +3307,12 @@ msgid "Manage Templates" msgstr "Vorlagen verwalten" #: editor/editor_node.cpp -#, fuzzy msgid "Install from file" -msgstr "Installiere aus Datei" +msgstr "Aus Datei installieren" #: editor/editor_node.cpp -#, fuzzy msgid "Select android sources file" -msgstr "Quell-Mesh auswählen:" +msgstr "Android-Quelldateien auswählen" #: editor/editor_node.cpp msgid "" @@ -3384,7 +3397,7 @@ msgstr "Auswählen" #: editor/editor_node.cpp #, fuzzy msgid "Select Current" -msgstr "Gegenwärtigen Ordner auswählen" +msgstr "Aktuelles auswählen" #: editor/editor_node.cpp msgid "Open 2D Editor" @@ -3419,9 +3432,8 @@ msgid "No sub-resources found." msgstr "Keine Unter-Ressourcen gefunden." #: editor/editor_path.cpp -#, fuzzy msgid "Open a list of sub-resources." -msgstr "Keine Unter-Ressourcen gefunden." +msgstr "Liste der Unter-Ressourcen öffnen." #: editor/editor_plugin.cpp msgid "Creating Mesh Previews" @@ -3448,14 +3460,12 @@ msgid "Update" msgstr "Update" #: editor/editor_plugin_settings.cpp -#, fuzzy msgid "Version" -msgstr "Version:" +msgstr "Version" #: editor/editor_plugin_settings.cpp -#, fuzzy msgid "Author" -msgstr "Autoren" +msgstr "Autor" #: editor/editor_plugin_settings.cpp #: editor/plugins/version_control_editor_plugin.cpp @@ -3468,14 +3478,12 @@ msgid "Measure:" msgstr "Messung:" #: editor/editor_profiler.cpp -#, fuzzy msgid "Frame Time (ms)" -msgstr "Renderzeit (Sek)" +msgstr "Frame-Zeit (ms)" #: editor/editor_profiler.cpp -#, fuzzy msgid "Average Time (ms)" -msgstr "Renderzeit ⌀ (sek)" +msgstr "Durchschnittszeit (ms)" #: editor/editor_profiler.cpp msgid "Frame %" @@ -3502,6 +3510,13 @@ msgid "" "functions called by that function.\n" "Use this to find individual functions to optimize." msgstr "" +"Inklusive: Beinhaltet Zeiten von anderen Funktionen die von dieser " +"aufgerufen wurden.\n" +"Brauchbar um Flaschenhälse zu finden.\n" +"\n" +"Eigen: Zählt nur die Zeit in dieser Funktion, nicht die Zeiten von ihr " +"aufgerufener Funktionen.\n" +"Brauchbar um einzelne Funktionen zum Optimieren zu finden." #: editor/editor_profiler.cpp msgid "Frame #:" @@ -3624,7 +3639,6 @@ msgid "Paste" msgstr "Einfügen" #: editor/editor_resource_picker.cpp editor/property_editor.cpp -#, fuzzy msgid "Convert to %s" msgstr "Umwandeln zu %s" @@ -3674,10 +3688,9 @@ msgid "Did you forget the '_run' method?" msgstr "Hast du die '_run' Methode vergessen?" #: editor/editor_spin_slider.cpp -#, fuzzy msgid "Hold %s to round to integers. Hold Shift for more precise changes." msgstr "" -"Strg-Taste halten um auf Ganzzahlen zu runden. Umschalt-Taste halten für " +"%s-Taste halten um auf Ganzzahlen zu runden. Umschalt-Taste halten für " "präzisere Änderungen." #: editor/editor_sub_scene.cpp @@ -3698,49 +3711,43 @@ msgstr "Aus Node importieren:" #: editor/export_template_manager.cpp msgid "Open the folder containing these templates." -msgstr "" +msgstr "Den Ordner der diese Exportvorlagen enthält öffnen." #: editor/export_template_manager.cpp msgid "Uninstall these templates." -msgstr "" +msgstr "Diese Exportvorlage deinstallieren." #: editor/export_template_manager.cpp -#, fuzzy msgid "There are no mirrors available." -msgstr "Datei ‚%s‘ existiert nicht." +msgstr "Keine Mirrors verfügbar." #: editor/export_template_manager.cpp -#, fuzzy msgid "Retrieving the mirror list..." -msgstr "Mirrors werden geladen, bitte warten..." +msgstr "Mirror-Liste werden abgerufen…" #: editor/export_template_manager.cpp msgid "Starting the download..." -msgstr "" +msgstr "Download wird begonnen…" #: editor/export_template_manager.cpp msgid "Error requesting URL:" msgstr "Fehler beim Abrufen der URL:" #: editor/export_template_manager.cpp -#, fuzzy msgid "Connecting to the mirror..." -msgstr "Verbinde mit Mirror..." +msgstr "Verbindung mit Mirror wird hergestellt..." #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't resolve the requested address." -msgstr "Kann Hostnamen nicht auflösen:" +msgstr "Angefragte Adresse konnte nicht aufgelöst werden." #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't connect to the mirror." -msgstr "Kann nicht zu Host verbinden:" +msgstr "Verbindung zu Mirror konnte nicht hergestellt werden." #: editor/export_template_manager.cpp -#, fuzzy msgid "No response from the mirror." -msgstr "Keine Antwort von Host:" +msgstr "Keine Antwort des Mirrors." #: editor/export_template_manager.cpp #: editor/plugins/asset_library_editor_plugin.cpp @@ -3748,18 +3755,16 @@ msgid "Request failed." msgstr "Anfrage fehlgeschlagen." #: editor/export_template_manager.cpp -#, fuzzy msgid "Request ended up in a redirect loop." -msgstr "Anfrage fehlgeschlagen, zu viele Weiterleitungen" +msgstr "Anfrage in Weiterleitungsschleife gefangen." #: editor/export_template_manager.cpp -#, fuzzy msgid "Request failed:" -msgstr "Anfrage fehlgeschlagen." +msgstr "Anfrage fehlgeschlagen:" #: editor/export_template_manager.cpp msgid "Download complete; extracting templates..." -msgstr "" +msgstr "Download abgeschlossen; Exportvorlagen werden extrahiert…" #: editor/export_template_manager.cpp msgid "Cannot remove temporary file:" @@ -3778,7 +3783,6 @@ msgid "Error getting the list of mirrors." msgstr "Fehler beim Laden der Spiegelserver." #: editor/export_template_manager.cpp -#, fuzzy msgid "Error parsing JSON with the list of mirrors. Please report this issue!" msgstr "" "Fehler beim Einlesen des JSON-Formats der Spiegelserverliste. Bitte diesen " @@ -3786,7 +3790,7 @@ msgstr "" #: editor/export_template_manager.cpp msgid "Best available mirror" -msgstr "" +msgstr "Bester verfügbarer Mirror" #: editor/export_template_manager.cpp msgid "" @@ -3839,24 +3843,20 @@ msgid "SSL Handshake Error" msgstr "SSL-Handshake-Fehler" #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't open the export templates file." -msgstr "Exportvorlagen-ZIP-Datei konnte nicht geöffnet werden." +msgstr "Exportvorlagendatei konnte nicht geöffnet werden." #: editor/export_template_manager.cpp -#, fuzzy msgid "Invalid version.txt format inside the export templates file: %s." -msgstr "Ungültiges version.txt-Format in Templates: %s." +msgstr "Ungültiges version.txt-Format in der Exportvorlagendatei: %s." #: editor/export_template_manager.cpp -#, fuzzy msgid "No version.txt found inside the export templates file." -msgstr "Keine version.txt in Templates gefunden." +msgstr "Keine version.txt in Exportvorlagendatei gefunden." #: editor/export_template_manager.cpp -#, fuzzy msgid "Error creating path for extracting templates:" -msgstr "Fehler bei Erzeugen des Pfads für die Vorlagen:" +msgstr "Fehler bei Erzeugen des Pfads zum Extrahieren der Exportvorlagen:" #: editor/export_template_manager.cpp msgid "Extracting Export Templates" @@ -3867,9 +3867,8 @@ msgid "Importing:" msgstr "Importiere:" #: editor/export_template_manager.cpp -#, fuzzy msgid "Remove templates for the version '%s'?" -msgstr "Template-Version ‚%s‘ entfernen?" +msgstr "Exportvorlagenversion ‚%s‘ entfernen?" #: editor/export_template_manager.cpp msgid "Uncompressing Android Build Sources" @@ -3885,44 +3884,54 @@ msgstr "Aktuelle Version:" #: editor/export_template_manager.cpp msgid "Export templates are missing. Download them or install from a file." -msgstr "" +msgstr "Exportvorlagen fehlen. Download oder Installation aus Datei nötig." #: editor/export_template_manager.cpp msgid "Export templates are installed and ready to be used." -msgstr "" +msgstr "Exportvorlagen sind installiert und bereit zum Verwenden." #: editor/export_template_manager.cpp -#, fuzzy msgid "Open Folder" -msgstr "Datei öffnen" +msgstr "Ordner öffnen" #: editor/export_template_manager.cpp msgid "Open the folder containing installed templates for the current version." msgstr "" +"Order mit den installierten Exportvorlagen der aktuellen Version öffnen." #: editor/export_template_manager.cpp msgid "Uninstall" msgstr "Deinstallieren" #: editor/export_template_manager.cpp -#, fuzzy msgid "Uninstall templates for the current version." -msgstr "Anfangswert für Zähler" +msgstr "Exportvorlagen der aktuellen Version deinstallieren." #: editor/export_template_manager.cpp -#, fuzzy msgid "Download from:" -msgstr "Downloadfehler" +msgstr "Herunterladen von:" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Im Browser ausführen" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "Fehlermeldung kopieren" #: editor/export_template_manager.cpp msgid "Download and Install" -msgstr "" +msgstr "Herunterladen und installieren" #: editor/export_template_manager.cpp msgid "" "Download and install templates for the current version from the best " "possible mirror." msgstr "" +"Exportvorlagen der aktuellen Version vom bestmöglichen Mirror herunterladen " +"und installieren." #: editor/export_template_manager.cpp msgid "Official export templates aren't available for development builds." @@ -3931,14 +3940,12 @@ msgstr "" "gestellt." #: editor/export_template_manager.cpp -#, fuzzy msgid "Install from File" -msgstr "Installiere aus Datei" +msgstr "Aus Datei installieren" #: editor/export_template_manager.cpp -#, fuzzy msgid "Install templates from a local file." -msgstr "Vorlagen aus ZIP-Datei importieren" +msgstr "Exportvorlagen aus lokaler Datei installieren." #: editor/export_template_manager.cpp editor/find_in_files.cpp #: editor/progress_dialog.cpp scene/gui/dialogs.cpp @@ -3946,19 +3953,16 @@ msgid "Cancel" msgstr "Abbrechen" #: editor/export_template_manager.cpp -#, fuzzy msgid "Cancel the download of the templates." -msgstr "Exportvorlagen-ZIP-Datei konnte nicht geöffnet werden." +msgstr "Herunterladen der Exportvorlagen abbrechen." #: editor/export_template_manager.cpp -#, fuzzy msgid "Other Installed Versions:" -msgstr "Installierte Versionen:" +msgstr "Andere installierte Versionen:" #: editor/export_template_manager.cpp -#, fuzzy msgid "Uninstall Template" -msgstr "Deinstallieren" +msgstr "Exportvorlage deinstallieren" #: editor/export_template_manager.cpp msgid "Select Template File" @@ -3973,6 +3977,8 @@ msgid "" "The templates will continue to download.\n" "You may experience a short editor freeze when they finish." msgstr "" +"Download der Exportvorlage wird fortgesetzt.\n" +"Der Editor kann nach Ende des Downloads kurzfristig stocken." #: editor/filesystem_dock.cpp msgid "Favorites" @@ -4119,35 +4125,32 @@ msgid "Collapse All" msgstr "Alle einklappen" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort files" -msgstr "Dateien suchen" +msgstr "Dateien sortieren" #: editor/filesystem_dock.cpp msgid "Sort by Name (Ascending)" -msgstr "" +msgstr "Nach Name sortieren (aufsteigend)" #: editor/filesystem_dock.cpp msgid "Sort by Name (Descending)" -msgstr "" +msgstr "Nach Name sortieren (absteigend)" #: editor/filesystem_dock.cpp msgid "Sort by Type (Ascending)" -msgstr "" +msgstr "Nach Typ sortieren (aufsteigend)" #: editor/filesystem_dock.cpp msgid "Sort by Type (Descending)" -msgstr "" +msgstr "Nach Typ sortieren (absteigend)" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort by Last Modified" -msgstr "Zuletzt bearbeitet" +msgstr "Nach Bearbeitungszeit sortieren (Aktuelles zuerst)" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort by First Modified" -msgstr "Zuletzt bearbeitet" +msgstr "Nach Bearbeitungszeit sortieren (Aktuelles zuletzt)" #: editor/filesystem_dock.cpp msgid "Duplicate..." @@ -4159,7 +4162,7 @@ msgstr "Umbenennen..." #: editor/filesystem_dock.cpp msgid "Focus the search box" -msgstr "" +msgstr "Suchfeld auswählen" #: editor/filesystem_dock.cpp msgid "Previous Folder/File" @@ -4471,14 +4474,12 @@ msgid "Failed to load resource." msgstr "Laden der Ressource gescheitert." #: editor/inspector_dock.cpp -#, fuzzy msgid "Copy Properties" -msgstr "Eigenschaften" +msgstr "Eigenschaften kopieren" #: editor/inspector_dock.cpp -#, fuzzy msgid "Paste Properties" -msgstr "Eigenschaften" +msgstr "Eigenschaften einfügen" #: editor/inspector_dock.cpp msgid "Make Sub-Resources Unique" @@ -4503,23 +4504,20 @@ msgid "Save As..." msgstr "Speichern als..." #: editor/inspector_dock.cpp -#, fuzzy msgid "Extra resource options." -msgstr "Nicht im Ressourcen-Pfad." +msgstr "Zusatz-Ressourcenoptionen." #: editor/inspector_dock.cpp -#, fuzzy msgid "Edit Resource from Clipboard" -msgstr "Ressourcen-Zwischenablage bearbeiten" +msgstr "Ressource in Zwischenablage bearbeiten" #: editor/inspector_dock.cpp msgid "Copy Resource" msgstr "Ressource kopieren" #: editor/inspector_dock.cpp -#, fuzzy msgid "Make Resource Built-In" -msgstr "Einbetten" +msgstr "Ressource zu Built-In konvertieren" #: editor/inspector_dock.cpp msgid "Go to the previous edited object in history." @@ -4534,9 +4532,8 @@ msgid "History of recently edited objects." msgstr "Verlauf der zuletzt bearbeiteten Objekte." #: editor/inspector_dock.cpp -#, fuzzy msgid "Open documentation for this object." -msgstr "Dokumentation öffnen" +msgstr "Dokumentation zu diesem Objekt öffnen." #: editor/inspector_dock.cpp editor/scene_tree_dock.cpp msgid "Open Documentation" @@ -4547,9 +4544,8 @@ msgid "Filter properties" msgstr "Eigenschaften filtern" #: editor/inspector_dock.cpp -#, fuzzy msgid "Manage object properties." -msgstr "Objekteigenschaften." +msgstr "Objekteigenschaften verwalten." #: editor/inspector_dock.cpp msgid "Changes may be lost!" @@ -4795,9 +4791,8 @@ msgid "Blend:" msgstr "Blende:" #: editor/plugins/animation_blend_tree_editor_plugin.cpp -#, fuzzy msgid "Parameter Changed:" -msgstr "Parameter geändert" +msgstr "Parameter geändert:" #: editor/plugins/animation_blend_tree_editor_plugin.cpp #: editor/plugins/animation_tree_player_editor_plugin.cpp @@ -5527,11 +5522,11 @@ msgstr "Alle" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Search templates, projects, and demos" -msgstr "" +msgstr "Vorlagen, Projekte und Demos durchsuchen" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Search assets (excluding templates, projects, and demos)" -msgstr "" +msgstr "Assets durchsuchen (ausgenommen Vorlagen, Projekte und Demos)" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Import..." @@ -5575,7 +5570,7 @@ msgstr "Nutzerinhalte als ZIP-Datei" #: editor/plugins/audio_stream_editor_plugin.cpp msgid "Audio Preview Play/Pause" -msgstr "" +msgstr "Audiovorschau abspielen/pausieren" #: editor/plugins/baked_lightmap_editor_plugin.cpp msgid "" @@ -5836,13 +5831,13 @@ msgstr "Ankerpunkte ändern" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "" "Project Camera Override\n" "Overrides the running project's camera with the editor viewport camera." msgstr "" -"Spielekamera überschreiben\n" -"Überschreibt die Spielekamera mit der Kamera des Anzeigefensters des Editors." +"Projektkamera überbrücken\n" +"Überbrückt die Kamera des laufenden Projekts mit der Kamera des " +"Anzeigefensters des Editors." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp @@ -5851,6 +5846,9 @@ msgid "" "No project instance running. Run the project from the editor to use this " "feature." msgstr "" +"Projektkamera überbrücken\n" +"Es wird zur Zeit keine Projektinstanz ausgeführt. Um diese Funktion zu " +"nutzen muss ein Projekt durch den Editor gestartet werden." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp @@ -5918,32 +5916,27 @@ msgstr "Auswahlmodus" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Drag: Rotate selected node around pivot." -msgstr "Ausgewählten Node oder Übergang entfernen." +msgstr "Ziehen: Ausgewähltes Node um Pivotpunkt rotieren." #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Alt+Drag: Move selected node." -msgstr "Alt+Ziehen = Verschieben" +msgstr "Alt+Ziehen = Ausgewähltes Node verschieben." #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "V: Set selected node's pivot position." -msgstr "Ausgewählten Node oder Übergang entfernen." +msgstr "V: Pivotpunkt des ausgewählten Nodes festlegen." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Alt+RMB: Show list of all nodes at position clicked, including locked." msgstr "" -"Zeige eine Liste aller Objekte, die sich an der angeklickten Position " -"befinden\n" -"(equivalent zu Alt+RMT im Auswahlmodus)." +"Alt+RMT: Liste aller Nodes an Klickposition anzeigen, einschließlich " +"gesperrter." #: editor/plugins/canvas_item_editor_plugin.cpp msgid "RMB: Add node at position clicked." -msgstr "" +msgstr "RMT: Node an Klickposition hinzufügen." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp @@ -6183,14 +6176,12 @@ msgid "Clear Pose" msgstr "Pose/Stellung löschen" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Add Node Here" -msgstr "Node hinzufügen" +msgstr "Node hier hinzufügen" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Instance Scene Here" -msgstr "Instanz-Szene(n)" +msgstr "Szene hier instantiieren" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Multiply grid step by 2" @@ -6206,49 +6197,43 @@ msgstr "Sicht verschieben" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 3.125%" -msgstr "" +msgstr "Auf 3.125% vergrößern" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 6.25%" -msgstr "" +msgstr "Auf 6.25% vergrößern" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 12.5%" -msgstr "" +msgstr "Auf 12.5% vergrößern" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 25%" -msgstr "Verkleinern" +msgstr "Auf 25% vergrößern" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 50%" -msgstr "Verkleinern" +msgstr "Auf 50% vergrößern" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 100%" -msgstr "Verkleinern" +msgstr "Auf 100% vergrößern" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 200%" -msgstr "Verkleinern" +msgstr "Auf 200% vergrößern" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 400%" -msgstr "Verkleinern" +msgstr "Auf 400% vergrößern" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 800%" -msgstr "Verkleinern" +msgstr "Auf 800% vergrößern" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 1600%" -msgstr "" +msgstr "Auf 1600% vergrößern" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Add %s" @@ -6495,9 +6480,8 @@ msgid "Couldn't create a single convex collision shape." msgstr "Ein einzelnes konvexes Kollisionselement konnte nicht erzeugt werden." #: editor/plugins/mesh_instance_editor_plugin.cpp -#, fuzzy msgid "Create Simplified Convex Shape" -msgstr "Einzelne konvexe Form erstellen" +msgstr "Vereinfachte konvexe Form erstellen" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Create Single Convex Shape" @@ -6535,9 +6519,8 @@ msgid "No mesh to debug." msgstr "Kein Mesh zu debuggen." #: editor/plugins/mesh_instance_editor_plugin.cpp -#, fuzzy msgid "Mesh has no UV in layer %d." -msgstr "Modell besitzt kein UV in dieser Schicht" +msgstr "Mesh hat kein UV in Schicht %d." #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "MeshInstance lacks a Mesh!" @@ -6602,16 +6585,19 @@ msgstr "" "Dies ist die schnellste (aber ungenauste) Methode für Kollisionsberechnungen." #: editor/plugins/mesh_instance_editor_plugin.cpp -#, fuzzy msgid "Create Simplified Convex Collision Sibling" -msgstr "Ein einzelnes konvexes Kollisionsnachbarelement erzeugen" +msgstr "Vereinfachtes konvexes Kollisionsnachbarelement erzeugen" #: editor/plugins/mesh_instance_editor_plugin.cpp +#, fuzzy msgid "" "Creates a simplified convex collision shape.\n" "This is similar to single collision shape, but can result in a simpler " "geometry in some cases, at the cost of accuracy." msgstr "" +"Erzeugt ein vereinfachtes konvexes Collision Shape.\n" +"Dies ist ähnlich zu einem einzigen Collision Shape, kann allerdings manchmal " +"zu einfacherer Geometrie führen, auf Kosten der Genauigkeit." #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Create Multiple Convex Collision Siblings" @@ -6624,8 +6610,9 @@ msgid "" "This is a performance middle-ground between a single convex collision and a " "polygon-based collision." msgstr "" -"Erstellt ein polygon-basiertes Kollisionselement.\n" -"Dies liegt von der Geschwindigkeit in der Mitte der beiden anderen Methoden." +"Erstellt ein polygon-basiertes Collision Shape.\n" +"Dies liegt von der Geschwindigkeit in der Mitte zwischen einer einzelnen " +"konvexen Kollision und einer polygon-basierten Kollision." #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Create Outline Mesh..." @@ -7271,24 +7258,20 @@ msgid "ResourcePreloader" msgstr "Ressourcen-Vorlader" #: editor/plugins/room_manager_editor_plugin.cpp -#, fuzzy msgid "Flip Portals" -msgstr "Horizontal umdrehen" +msgstr "Portale umdrehen" #: editor/plugins/room_manager_editor_plugin.cpp -#, fuzzy msgid "Room Generate Points" -msgstr "Anzahl generierter Punkte:" +msgstr "Generiere Punkte mittels Room" #: editor/plugins/room_manager_editor_plugin.cpp -#, fuzzy msgid "Generate Points" -msgstr "Anzahl generierter Punkte:" +msgstr "Generiere Punkte" #: editor/plugins/room_manager_editor_plugin.cpp -#, fuzzy msgid "Flip Portal" -msgstr "Horizontal umdrehen" +msgstr "Portal umdrehen" #: editor/plugins/root_motion_editor_plugin.cpp msgid "AnimationTree has no path set to an AnimationPlayer" @@ -7854,20 +7837,17 @@ msgid "None" msgstr "Nichts" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Rotate" -msgstr "Status" +msgstr "Rotierung" #. TRANSLATORS: This refers to the movement that changes the position of an object. #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Translate" -msgstr "Translation:" +msgstr "Verschiebung" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Scale" -msgstr "Skalierung:" +msgstr "Skalierung" #: editor/plugins/spatial_editor_plugin.cpp msgid "Scaling: " @@ -7890,52 +7870,44 @@ msgid "Animation Key Inserted." msgstr "Animationsschlüsselbild eingefügt." #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Pitch:" -msgstr "Neigen" +msgstr "Neigung:" #: editor/plugins/spatial_editor_plugin.cpp msgid "Yaw:" -msgstr "" +msgstr "Gierung:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Size:" -msgstr "Größe: " +msgstr "Größe:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Objects Drawn:" -msgstr "Gezeichnete Objekte" +msgstr "Gezeichnete Objekte:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Material Changes:" -msgstr "Materialänderungen" +msgstr "Materialänderungen:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Shader Changes:" -msgstr "Shader-Änderungen" +msgstr "Shader-Änderungen:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Surface Changes:" -msgstr "Oberflächen-Änderungen" +msgstr "Oberflächen-Änderungen:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Draw Calls:" -msgstr "Zeichenaufrufe" +msgstr "Zeichenaufrufe:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Vertices:" -msgstr "Eckpunkte" +msgstr "Eckpunkte:" #: editor/plugins/spatial_editor_plugin.cpp msgid "FPS: %d (%s ms)" -msgstr "" +msgstr "FPS: %d (%s ms)" #: editor/plugins/spatial_editor_plugin.cpp msgid "Top View." @@ -8091,9 +8063,8 @@ msgid "Freelook Slow Modifier" msgstr "Freisicht Trägheitsregler" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Toggle Camera Preview" -msgstr "Ändere Kameragröße" +msgstr "Kameravorschau umschalten" #: editor/plugins/spatial_editor_plugin.cpp msgid "View Rotation Locked" @@ -8115,9 +8086,8 @@ msgstr "" "Sie ist kein zuverlässiger Vergleichswert für die In-Spiel-Leistung." #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Convert Rooms" -msgstr "Umwandeln zu %s" +msgstr "Räume umwandeln" #: editor/plugins/spatial_editor_plugin.cpp msgid "XForm Dialog" @@ -8139,7 +8109,6 @@ msgstr "" "(\"Röntgenblick\")." #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Snap Nodes to Floor" msgstr "Nodes am Boden einrasten" @@ -8159,7 +8128,7 @@ msgstr "Einrasten aktivieren" #: editor/plugins/spatial_editor_plugin.cpp msgid "Converts rooms for portal culling." -msgstr "" +msgstr "Räume für Portal-Culling konvertieren." #: editor/plugins/spatial_editor_plugin.cpp msgid "Bottom View" @@ -8255,9 +8224,8 @@ msgid "View Grid" msgstr "Zeige Gitter" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "View Portal Culling" -msgstr "Einstellungen für Ansichten" +msgstr "Portal-Culling anzeigen" #: editor/plugins/spatial_editor_plugin.cpp #: modules/gridmap/grid_map_editor_plugin.cpp @@ -8579,221 +8547,196 @@ msgid "TextureRegion" msgstr "Texturbereich" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Colors" -msgstr "Farbe" +msgstr "Farben" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Fonts" -msgstr "Schriftart" +msgstr "Schriftarten" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Icons" -msgstr "Symbol" +msgstr "Symbole" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Styleboxes" -msgstr "Style-Box" +msgstr "Style-Boxen" #: editor/plugins/theme_editor_plugin.cpp msgid "{num} color(s)" -msgstr "" +msgstr "{num} Farbe(n)" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No colors found." -msgstr "Keine Unter-Ressourcen gefunden." +msgstr "Keine Farben gefunden." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "{num} constant(s)" -msgstr "Konstanten" +msgstr "{num} Konstante(n)" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No constants found." -msgstr "Farbkonstante." +msgstr "Keine Konstanten gefunden." #: editor/plugins/theme_editor_plugin.cpp msgid "{num} font(s)" -msgstr "" +msgstr "{num} Schriftart(en)" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No fonts found." -msgstr "Nicht gefunden!" +msgstr "Keine Schriftarten gefunden." #: editor/plugins/theme_editor_plugin.cpp msgid "{num} icon(s)" -msgstr "" +msgstr "{num} Symbol(e)" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No icons found." -msgstr "Nicht gefunden!" +msgstr "Keine Symbole gefunden." #: editor/plugins/theme_editor_plugin.cpp msgid "{num} stylebox(es)" -msgstr "" +msgstr "{num} Style-Box(en)" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No styleboxes found." -msgstr "Keine Unter-Ressourcen gefunden." +msgstr "Keine Style-Boxen gefunden." #: editor/plugins/theme_editor_plugin.cpp msgid "{num} currently selected" -msgstr "" +msgstr "{num} zurzeit ausgewählt" #: editor/plugins/theme_editor_plugin.cpp msgid "Nothing was selected for the import." -msgstr "" +msgstr "Es wurde nichts zum Importieren ausgewählt." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Importing Theme Items" -msgstr "Theme importieren" +msgstr "Am Importieren von Thema-Elementen" #: editor/plugins/theme_editor_plugin.cpp msgid "Importing items {n}/{n}" -msgstr "" +msgstr "Am Importieren von Elementen {n}/{n}" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Updating the editor" -msgstr "Editor verlassen?" +msgstr "Den Editor aktualisieren?" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Finalizing" -msgstr "Analysiere" +msgstr "Fertigstellen" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Filter:" -msgstr "Filter: " +msgstr "Filter:" #: editor/plugins/theme_editor_plugin.cpp msgid "With Data" -msgstr "" +msgstr "Mit Daten" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select by data type:" -msgstr "Node auswählen" +msgstr "Nach Datentyp auswählen:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible color items." -msgstr "Teilung zum Löschen auswählen." +msgstr "Alle sichtbaren Farbelemente auswählen." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible color items and their data." -msgstr "" +msgstr "Alle sichtbaren Farbelemente und deren Daten auswählen." #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all visible color items." -msgstr "" +msgstr "Alle sichtbaren Farbelemente abwählen." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible constant items." -msgstr "Zuerst Einstellungspunkt auswählen!" +msgstr "Alle sichtbaren konstanten Elemente auswählen." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible constant items and their data." -msgstr "" +msgstr "Alle sichtbaren konstanten Elemente und ihre Daten auswählen." #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all visible constant items." -msgstr "" +msgstr "Alle sichtbaren konstanten Elemente abwählen." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible font items." -msgstr "Zuerst Einstellungspunkt auswählen!" +msgstr "Alle sichtbaren Schriftart-Elemente auswählen." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible font items and their data." -msgstr "" +msgstr "Alle sichtbaren Schriftart-Elemente und ihre Daten auswählen." #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all visible font items." -msgstr "" +msgstr "Alle sichtbaren Schriftart-Elemente abwählen." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible icon items." -msgstr "Zuerst Einstellungspunkt auswählen!" +msgstr "Alle sichtbaren Symbolelemente auswählen." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible icon items and their data." -msgstr "Zuerst Einstellungspunkt auswählen!" +msgstr "Alle sichtbaren Symbolelemente und ihre Daten auswählen." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Deselect all visible icon items." -msgstr "Zuerst Einstellungspunkt auswählen!" +msgstr "Alle sichtbaren Symbolelemente abwählen." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible stylebox items." -msgstr "" +msgstr "Alle sichtbaren Style-Box-Elemente auswählen." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible stylebox items and their data." -msgstr "" +msgstr "Alle sichtbaren Style-Box-Elemente und ihre Daten auswählen." #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all visible stylebox items." -msgstr "" +msgstr "Alle sichtbaren Style-Box-Elemente abwählen." #: editor/plugins/theme_editor_plugin.cpp msgid "" "Caution: Adding icon data may considerably increase the size of your Theme " "resource." msgstr "" +"Vorsicht: Das Hinzufügen von Symbolelement-Daten kann die Thema-Ressource " +"erheblich vergrößern." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Collapse types." -msgstr "Alle einklappen" +msgstr "Typen einklappen." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Expand types." -msgstr "Alle ausklappen" +msgstr "Typen ausklappen." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all Theme items." -msgstr "Vorlagendatei auswählen" +msgstr "Alle Thema-Elemente auswählen." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select With Data" -msgstr "Punkte auswählen" +msgstr "Mit Daten auswählen" #: editor/plugins/theme_editor_plugin.cpp msgid "Select all Theme items with item data." -msgstr "" +msgstr "Alle Thema-Elemente mit Element-Daten auswählen." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Deselect All" -msgstr "Alles auswählen" +msgstr "Alles abwählen" #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all Theme items." -msgstr "" +msgstr "Alle Themen-Elemente abwählen." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Import Selected" -msgstr "Szene importieren" +msgstr "Ausgewähltes importieren" #: editor/plugins/theme_editor_plugin.cpp msgid "" @@ -8801,271 +8744,245 @@ msgid "" "closing this window.\n" "Close anyway?" msgstr "" +"Es sind Elemente im „Elemente importieren“-Tab ausgewählt. Diese Auswahl " +"wird beim Schließen des Fensters verloren gehen.\n" +"Trotzdem schließen?" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All Color Items" -msgstr "Alle Elemente entfernen" +msgstr "Alle Farbelemente entfernen" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Item" -msgstr "Entferne Element" +msgstr "Element umbenennen" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All Constant Items" -msgstr "Alle Elemente entfernen" +msgstr "Alle konstanten Elemente entfernen" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All Font Items" -msgstr "Alle Elemente entfernen" +msgstr "Alle Schriftart-Elemente entfernen" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All Icon Items" -msgstr "Alle Elemente entfernen" +msgstr "Alle Symbol-Elemente entfernen" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All StyleBox Items" -msgstr "Alle Elemente entfernen" +msgstr "Alle StyleBox-Elemente entfernen" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Color Item" -msgstr "Füge Klassen-Element hinzu" +msgstr "Farbelement hinzufügen" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Constant Item" -msgstr "Füge Klassen-Element hinzu" +msgstr "Konstantes Element hinzufügen" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Font Item" -msgstr "Element hinzufügen" +msgstr "Schriftart-Element hinzufügen" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Icon Item" -msgstr "Element hinzufügen" +msgstr "Symbol-Element hinzufügen" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Stylebox Item" -msgstr "Alle Elemente hinzufügen" +msgstr "StyleBox-Element hinzufügen" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Color Item" -msgstr "Entferne Klassen-Element" +msgstr "Farbelement umbenennen" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Constant Item" -msgstr "Entferne Klassen-Element" +msgstr "Konstantes Element umbenennen" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Font Item" -msgstr "Node umbenennen" +msgstr "Schriftart-Element umbenennen" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Icon Item" -msgstr "Node umbenennen" +msgstr "Symbol-Element umbenennen" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Stylebox Item" -msgstr "Ausgewähltes Element entfernen" +msgstr "StyleBox-Element umbenennen" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Invalid file, not a Theme resource." -msgstr "Ungültige Datei, kein Audiobus-Layout." +msgstr "Ungültige Datei, keine Thema-Ressource." #: editor/plugins/theme_editor_plugin.cpp msgid "Invalid file, same as the edited Theme resource." -msgstr "" +msgstr "Ungültige Datei, ist identisch mit der bearbeiteten Thema-Ressource." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Manage Theme Items" -msgstr "Vorlagen verwalten" +msgstr "Thema-Elemente verwalten" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Edit Items" -msgstr "Bearbeitbares Element" +msgstr "Element bearbeiten" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Types:" -msgstr "Typ:" +msgstr "Typen:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Type:" -msgstr "Typ:" +msgstr "Typ hinzufügen:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Item:" -msgstr "Element hinzufügen" +msgstr "Element hinzufügen:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add StyleBox Item" -msgstr "Alle Elemente hinzufügen" +msgstr "StyleBox-Element hinzufügen" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove Items:" -msgstr "Entferne Element" +msgstr "Elemente entfernen:" #: editor/plugins/theme_editor_plugin.cpp msgid "Remove Class Items" msgstr "Entferne Klassen-Element" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove Custom Items" -msgstr "Entferne Klassen-Element" +msgstr "Eigene Elemente entfernen" #: editor/plugins/theme_editor_plugin.cpp msgid "Remove All Items" msgstr "Alle Elemente entfernen" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Theme Item" -msgstr "GUI-Thema-Elemente" +msgstr "Thema-Element hinzufügen" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Old Name:" -msgstr "Node-Name:" +msgstr "Alter Name:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Import Items" -msgstr "Theme importieren" +msgstr "Elemente importieren" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Default Theme" -msgstr "Standard" +msgstr "Standard-Thema" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Editor Theme" -msgstr "Thema bearbeiten" +msgstr "Editor-Motiv" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select Another Theme Resource:" -msgstr "Ressource löschen" +msgstr "Andere Thema-Ressource auswählen:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Another Theme" -msgstr "Theme importieren" +msgstr "Anderes Design" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Confirm Item Rename" -msgstr "Spur umbenennen" +msgstr "Elementumbenennung bestätigen" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Cancel Item Rename" -msgstr "Stapelweise Umbenennung" +msgstr "Elementumbenennung abbrechen" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Override Item" -msgstr "Überschreibungen" +msgstr "Element überbrücken" #: editor/plugins/theme_editor_plugin.cpp msgid "Unpin this StyleBox as a main style." -msgstr "" +msgstr "Diese StyleBox nicht mehr als Hauptstil markieren." #: editor/plugins/theme_editor_plugin.cpp msgid "" "Pin this StyleBox as a main style. Editing its properties will update the " "same properties in all other StyleBoxes of this type." msgstr "" +"Diese StyleBox als Hauptstil markieren. Geänderte Eigenschaften dieser " +"StyleBox werden ebenfalls in allen StyleBoxen des gleichen Typs geändert." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Type" -msgstr "Art" +msgstr "Typ hinzufügen" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Item Type" -msgstr "Element hinzufügen" +msgstr "Elementtyp hinzufügen" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Node Types:" -msgstr "Node-Typ" +msgstr "Node-Typen:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Show Default" -msgstr "Standard laden" +msgstr "Standard anzeigen" #: editor/plugins/theme_editor_plugin.cpp msgid "Show default type items alongside items that have been overridden." -msgstr "" +msgstr "Standard Typelemente zusammen mit überbrückten Elementen anzeigen." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Override All" -msgstr "Überschreibungen" +msgstr "Alle überbrücken" #: editor/plugins/theme_editor_plugin.cpp msgid "Override all default type items." -msgstr "" +msgstr "Alle Standard-Typelemente überbrücken." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Theme:" -msgstr "Designvorlagen (Thema)" +msgstr "Thema:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Manage Items..." -msgstr "Exportvorlagen verwalten…" +msgstr "Elemente verwalten…" #: editor/plugins/theme_editor_plugin.cpp msgid "Add, remove, organize and import Theme items." -msgstr "" +msgstr "Themenelemente hinzufügen, entfernen, verwalten und importieren." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Preview" -msgstr "Vorschau" +msgstr "Vorschau hinzufügen" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Default Preview" -msgstr "Vorschau aktualisieren" +msgstr "Standard-Vorschau" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select UI Scene:" -msgstr "Quell-Mesh auswählen:" +msgstr "UI-Szene auswählen:" #: editor/plugins/theme_editor_preview.cpp msgid "" "Toggle the control picker, allowing to visually select control types for " "edit." msgstr "" +"Auswahlmodus für Control-Nodes umschalten. Erlaubt das visuelle Auswählen " +"von Control-Nodes zum Bearbeiten." #: editor/plugins/theme_editor_preview.cpp msgid "Toggle Button" @@ -9100,9 +9017,8 @@ msgid "Checked Radio Item" msgstr "Markiertes Element der Auswahl" #: editor/plugins/theme_editor_preview.cpp -#, fuzzy msgid "Named Separator" -msgstr "Ben. Trenner." +msgstr "Benannter Trenner" #: editor/plugins/theme_editor_preview.cpp msgid "Submenu" @@ -9155,19 +9071,21 @@ msgstr "Hat,Mehrere,Einstellungen" #: editor/plugins/theme_editor_preview.cpp msgid "Invalid path, the PackedScene resource was probably moved or removed." msgstr "" +"Ungültiger Pfad. Die PackedScene-Ressource wurde vermutlich verschoben oder " +"gelöscht." #: editor/plugins/theme_editor_preview.cpp msgid "Invalid PackedScene resource, must have a Control node at its root." msgstr "" +"Ungültige PackedScene-Ressource. Muss ein Control-Node als Wurzel haben." #: editor/plugins/theme_editor_preview.cpp -#, fuzzy msgid "Invalid file, not a PackedScene resource." -msgstr "Ungültige Datei, kein Audiobus-Layout." +msgstr "Ungültige Datei, keine PackedScene-Ressource." #: editor/plugins/theme_editor_preview.cpp msgid "Reload the scene to reflect its most actual state." -msgstr "" +msgstr "Die Szene neu laden um ihren aktuellsten Status widerzuspiegeln." #: editor/plugins/tile_map_editor_plugin.cpp msgid "Erase Selection" @@ -10574,9 +10492,8 @@ msgid "VisualShader" msgstr "VisuellerShader" #: editor/plugins/visual_shader_editor_plugin.cpp -#, fuzzy msgid "Edit Visual Property:" -msgstr "Visuelle Eigenschaft bearbeiten" +msgstr "Visuelle Eigenschaft bearbeiten:" #: editor/plugins/visual_shader_editor_plugin.cpp msgid "Visual Shader Mode Changed" @@ -10701,9 +10618,8 @@ msgid "Script" msgstr "Skript" #: editor/project_export.cpp -#, fuzzy msgid "GDScript Export Mode:" -msgstr "Skript-Exportmodus:" +msgstr "GDScript-Exportmodus:" #: editor/project_export.cpp msgid "Text" @@ -10711,21 +10627,21 @@ msgstr "Text" #: editor/project_export.cpp msgid "Compiled Bytecode (Faster Loading)" -msgstr "" +msgstr "Kompilierter Bytecode (schnelleres Laden)" #: editor/project_export.cpp msgid "Encrypted (Provide Key Below)" msgstr "Verschlüsselt (Schlüssel unten angeben)" #: editor/project_export.cpp -#, fuzzy msgid "Invalid Encryption Key (must be 64 hexadecimal characters long)" -msgstr "Ungültiger Schlüssel für Verschlüsselung (muss 64 Zeichen lang sein)" +msgstr "" +"Ungültiger Schlüssel für Verschlüsselung (muss aus 64 hexadezimalen Zeichen " +"bestehen)" #: editor/project_export.cpp -#, fuzzy msgid "GDScript Encryption Key (256-bits as hexadecimal):" -msgstr "Skript-Schlüssel (256 Bit hexadezimal):" +msgstr "Schlüssel zur GDScript-Verschlüsselung (256 bit, hexadezimal):" #: editor/project_export.cpp msgid "Export PCK/Zip" @@ -10797,7 +10713,6 @@ msgid "Imported Project" msgstr "Importiertes Projekt" #: editor/project_manager.cpp -#, fuzzy msgid "Invalid project name." msgstr "Ungültiger Projektname." @@ -11027,14 +10942,12 @@ msgid "Are you sure to run %d projects at once?" msgstr "Sollen wirklich %d Projekte gleichzeitig ausgeführt werden?" #: editor/project_manager.cpp -#, fuzzy msgid "Remove %d projects from the list?" -msgstr "Gerät aus Liste auswählen" +msgstr "%d Projekte aus Liste entfernen?" #: editor/project_manager.cpp -#, fuzzy msgid "Remove this project from the list?" -msgstr "Gerät aus Liste auswählen" +msgstr "Dieses Projekt von Liste entfernen?" #: editor/project_manager.cpp msgid "" @@ -11067,9 +10980,8 @@ msgid "Project Manager" msgstr "Projektverwaltung" #: editor/project_manager.cpp -#, fuzzy msgid "Local Projects" -msgstr "Projekte" +msgstr "Lokale Projekte" #: editor/project_manager.cpp msgid "Loading, please wait..." @@ -11080,23 +10992,20 @@ msgid "Last Modified" msgstr "Zuletzt bearbeitet" #: editor/project_manager.cpp -#, fuzzy msgid "Edit Project" -msgstr "Projekt exportieren" +msgstr "Projekt bearbeiten" #: editor/project_manager.cpp -#, fuzzy msgid "Run Project" -msgstr "Projekt umbenennen" +msgstr "Projekt ausführen" #: editor/project_manager.cpp msgid "Scan" msgstr "Scannen" #: editor/project_manager.cpp -#, fuzzy msgid "Scan Projects" -msgstr "Projekte" +msgstr "Nach Projekten suchen" #: editor/project_manager.cpp msgid "Select a Folder to Scan" @@ -11107,14 +11016,12 @@ msgid "New Project" msgstr "Neues Projekt" #: editor/project_manager.cpp -#, fuzzy msgid "Import Project" -msgstr "Importiertes Projekt" +msgstr "Projekt importieren" #: editor/project_manager.cpp -#, fuzzy msgid "Remove Project" -msgstr "Projekt umbenennen" +msgstr "Projekt entfernen" #: editor/project_manager.cpp msgid "Remove Missing" @@ -11125,9 +11032,8 @@ msgid "About" msgstr "Über" #: editor/project_manager.cpp -#, fuzzy msgid "Asset Library Projects" -msgstr "Bestandsbibliothek (AssetLib)" +msgstr "Nutzerinhalte-Projekte" #: editor/project_manager.cpp msgid "Restart Now" @@ -11139,7 +11045,7 @@ msgstr "Alles entfernen" #: editor/project_manager.cpp msgid "Also delete project contents (no undo!)" -msgstr "" +msgstr "Ebenfalls Projektinhalte löschen (nicht rückgängig machbar!)" #: editor/project_manager.cpp msgid "Can't run project" @@ -11155,21 +11061,19 @@ msgstr "" "werden?" #: editor/project_manager.cpp -#, fuzzy msgid "Filter projects" -msgstr "Eigenschaften filtern" +msgstr "Projekte filtern" #: editor/project_manager.cpp -#, fuzzy msgid "" "This field filters projects by name and last path component.\n" "To filter projects by name and full path, the query must contain at least " "one `/` character." msgstr "" -"Die Suchmaske filtert Projekte nach ihrem Namen oder der letzten Komponente " -"ihres Pfadnamens.\n" -"Um den Filter auf den gesamten Pfadnamen anzuwenden muss mindestens ein ‚/‘-" -"Zeichen in der Suchanfrage vorhanden sein." +"Diese Suchmaske filtert Projekte nach ihrem Namen oder der letzten " +"Komponente ihres Pfadnamens.\n" +"Um nach dem kompletten Pfad zu filtern muss mindestens ein ‚/‘-Zeichen in " +"der Suchanfrage vorhanden sein." #: editor/project_settings_editor.cpp msgid "Key " @@ -11177,7 +11081,7 @@ msgstr "Taste " #: editor/project_settings_editor.cpp msgid "Physical Key" -msgstr "" +msgstr "Physische Taste" #: editor/project_settings_editor.cpp msgid "Joy Button" @@ -11225,7 +11129,7 @@ msgstr "Gerät" #: editor/project_settings_editor.cpp msgid " (Physical)" -msgstr "" +msgstr " (physisch)" #: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp msgid "Press a Key..." @@ -11368,23 +11272,20 @@ msgid "Override for Feature" msgstr "Für Funktion überschreiben" #: editor/project_settings_editor.cpp -#, fuzzy msgid "Add %d Translations" -msgstr "Übersetzung hinzufügen" +msgstr "%d Übersetzungen hinzufügen" #: editor/project_settings_editor.cpp msgid "Remove Translation" msgstr "Übersetzung entfernen" #: editor/project_settings_editor.cpp -#, fuzzy msgid "Translation Resource Remap: Add %d Path(s)" -msgstr "Ressourcen-Umleitung hinzufügen" +msgstr "Übersetzungsressourcenumschreibung: %d Pfad(e) hinzufügen" #: editor/project_settings_editor.cpp -#, fuzzy msgid "Translation Resource Remap: Add %d Remap(s)" -msgstr "Ressourcen-Umleitung hinzufügen" +msgstr "Übersetzungsressourcenumschreibung: %d Umschreibung(en) hinzufügen" #: editor/project_settings_editor.cpp msgid "Change Resource Remap Language" @@ -11831,12 +11732,16 @@ msgstr "Node „%s“ löschen?" msgid "" "Saving the branch as a scene requires having a scene open in the editor." msgstr "" +"Um den Zweig als Szene speichern zu können muss eine Szene im Editor " +"geöffnet sein." #: editor/scene_tree_dock.cpp msgid "" "Saving the branch as a scene requires selecting only one node, but you have " "selected %d nodes." msgstr "" +"Um den Zweig als Szene speichern zu können darf nur ein Node ausgewählt " +"sein. Es sind allerdings %d Nodes ausgewählt." #: editor/scene_tree_dock.cpp msgid "" @@ -11845,6 +11750,11 @@ msgid "" "FileSystem dock context menu\n" "or create an inherited scene using Scene > New Inherited Scene... instead." msgstr "" +"Der Wurzel-Node-Zweig kann nicht als instantiierte Szene gespeichert " +"werden.\n" +"Eine bearbeitbare Kopie der aktuellen Szene kann im Kontextmenü der " +"Dateisystemleiste\n" +"oder im Menü unter „Szene > Neue geerbte Szene…“ erstellt werden." #: editor/scene_tree_dock.cpp msgid "" @@ -11852,6 +11762,10 @@ msgid "" "To create a variation of a scene, you can make an inherited scene based on " "the instanced scene using Scene > New Inherited Scene... instead." msgstr "" +"Zeig einer bereits instantiierten Szene kann nicht gespeichert werden.\n" +"Ein Abwandlung der Szene zu erstellen, kann eine geerbten Szene, basiert auf " +"der instantiierten Szene, im Menü unter „Szene > Neue geerbte Szene…“ " +"erzeugt werden." #: editor/scene_tree_dock.cpp msgid "Save New Scene As..." @@ -12260,6 +12174,8 @@ msgid "" "Warning: Having the script name be the same as a built-in type is usually " "not desired." msgstr "" +"Hinweis: Der Skriptname ist identisch mit dem Name eines Built-In-Typs, " +"üblicherweise ein Fehler." #: editor/script_create_dialog.cpp msgid "Class Name:" @@ -12331,7 +12247,7 @@ msgstr "Fehlermeldung kopieren" #: editor/script_editor_debugger.cpp msgid "Open C++ Source on GitHub" -msgstr "" +msgstr "C++-Quelldatei auf GitHub aufrufen" #: editor/script_editor_debugger.cpp msgid "Video RAM" @@ -12509,6 +12425,16 @@ msgstr "Zylinderformhöhe ändern" msgid "Change Ray Shape Length" msgstr "Ändere Länge der Strahlenform" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "Kurvenpunktposition festlegen" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "Kurvenpunktposition festlegen" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "Zylinderradius ändern" @@ -12620,14 +12546,12 @@ msgid "Object can't provide a length." msgstr "Objekt kann keine Länge vorweisen." #: modules/gltf/editor_scene_exporter_gltf_plugin.cpp -#, fuzzy msgid "Export Mesh GLTF2" -msgstr "MeshLibrary exportieren" +msgstr "GLTF2-Mesh exportieren" #: modules/gltf/editor_scene_exporter_gltf_plugin.cpp -#, fuzzy msgid "Export GLTF..." -msgstr "Exportieren..." +msgstr "GLTF exportieren..." #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Next Plane" @@ -12670,9 +12594,8 @@ msgid "GridMap Paint" msgstr "GridMap zeichnen" #: modules/gridmap/grid_map_editor_plugin.cpp -#, fuzzy msgid "GridMap Selection" -msgstr "GridMap-Auswahl füllen" +msgstr "GridMap-Auswahl" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Grid Map" @@ -12925,14 +12848,12 @@ msgid "Add Output Port" msgstr "Ausgangsschnittstelle hinzufügen" #: modules/visual_script/visual_script_editor.cpp -#, fuzzy msgid "Change Port Type" -msgstr "Typ ändern" +msgstr "Schnittstellentyp ändern" #: modules/visual_script/visual_script_editor.cpp -#, fuzzy msgid "Change Port Name" -msgstr "Eingangsschnittstellenname ändern" +msgstr "Schnittstellenname ändern" #: modules/visual_script/visual_script_editor.cpp msgid "Override an existing built-in function." @@ -13047,9 +12968,8 @@ msgid "Add Preload Node" msgstr "Preload-Node hinzufügen" #: modules/visual_script/visual_script_editor.cpp -#, fuzzy msgid "Add Node(s)" -msgstr "Node hinzufügen" +msgstr "Node(s) hinzufügen" #: modules/visual_script/visual_script_editor.cpp msgid "Add Node(s) From Tree" @@ -13316,37 +13236,31 @@ msgstr "Gerät aus Liste auswählen" #: platform/android/export/export.cpp msgid "Running on %s" -msgstr "" +msgstr "Läuft auf %s" #: platform/android/export/export.cpp -#, fuzzy msgid "Exporting APK..." -msgstr "Exportiere alles" +msgstr "APK exportieren…" #: platform/android/export/export.cpp -#, fuzzy msgid "Uninstalling..." -msgstr "Deinstallieren" +msgstr "Am Deinstallieren…" #: platform/android/export/export.cpp -#, fuzzy msgid "Installing to device, please wait..." -msgstr "Projekte werden geladen, bitte warten..." +msgstr "Am Installieren auf Gerät, bitte warten..." #: platform/android/export/export.cpp -#, fuzzy msgid "Could not install to device: %s" -msgstr "Konnte Szene nicht instantiieren!" +msgstr "Konnte Installation auf Gerät nicht durchführen: %s" #: platform/android/export/export.cpp -#, fuzzy msgid "Running on device..." -msgstr "Angepasstes Skript wird ausgeführt..." +msgstr "Auf Gerät ausführen…" #: platform/android/export/export.cpp -#, fuzzy msgid "Could not execute on device." -msgstr "Ordner konnte nicht erstellt werden." +msgstr "Ließ sich nicht auf Gerät ausführen." #: platform/android/export/export.cpp msgid "Unable to find the 'apksigner' tool." @@ -13475,40 +13389,37 @@ msgid "" "directory.\n" "The resulting %s is unsigned." msgstr "" +"‚apksigner‘ konnte nicht gefunden werden.\n" +"Ist das Programm im Android SDK build-tools-Verzeichnis vorhanden?\n" +"Das resultierende %s ist nicht signiert." #: platform/android/export/export.cpp msgid "Signing debug %s..." -msgstr "" +msgstr "Signiere Debug-Build %s…" #: platform/android/export/export.cpp -#, fuzzy msgid "Signing release %s..." -msgstr "" -"Lese Dateien,\n" -"Bitte warten..." +msgstr "Signiere Release-Build %s…" #: platform/android/export/export.cpp -#, fuzzy msgid "Could not find keystore, unable to export." -msgstr "Konnte Vorlage nicht zum Export öffnen:" +msgstr "Keystore konnte nicht gefunden werden, Export fehlgeschlagen." #: platform/android/export/export.cpp msgid "'apksigner' returned with error #%d" -msgstr "" +msgstr "‚apksigner‘ gab Fehlercode #%d zurück" #: platform/android/export/export.cpp -#, fuzzy msgid "Verifying %s..." -msgstr "%s hinzufügen…" +msgstr "Verifiziere %s…" #: platform/android/export/export.cpp msgid "'apksigner' verification of %s failed." -msgstr "" +msgstr "‚apksigner‘-Verifizierung von %s fehlgeschlagen." #: platform/android/export/export.cpp -#, fuzzy msgid "Exporting for Android" -msgstr "Exportiere alles" +msgstr "Exportiere für Android" #: platform/android/export/export.cpp msgid "Invalid filename! Android App Bundle requires the *.aab extension." @@ -13527,7 +13438,7 @@ msgstr "" #: platform/android/export/export.cpp msgid "Unsupported export format!\n" -msgstr "" +msgstr "Nicht unterstütztes Exportformat!\n" #: platform/android/export/export.cpp msgid "" @@ -13554,16 +13465,16 @@ msgstr "" msgid "" "Unable to overwrite res://android/build/res/*.xml files with project name" msgstr "" +"Kann res://android/build/res/*.xml Dateien nicht mit Projektnamen " +"überschreiben" #: platform/android/export/export.cpp -#, fuzzy msgid "Could not export project files to gradle project\n" -msgstr "project.godot konnte nicht im Projektpfad gefunden werden." +msgstr "Konnte Projektdateien nicht als Gradle-Projekt exportieren\n" #: platform/android/export/export.cpp -#, fuzzy msgid "Could not write expansion package file!" -msgstr "Konnte Datei nicht schreiben:" +msgstr "Konnte Expansion-Package-Datei nicht schreiben!" #: platform/android/export/export.cpp msgid "Building Android Project (gradle)" @@ -13592,21 +13503,20 @@ msgstr "" "im Gradle Projektverzeichnis erscheinen." #: platform/android/export/export.cpp -#, fuzzy msgid "Package not found: %s" -msgstr "Animation nicht gefunden: ‚%s‘" +msgstr "Paket nicht gefunden: %s" #: platform/android/export/export.cpp -#, fuzzy msgid "Creating APK..." -msgstr "Konturen erzeugen..." +msgstr "Erzeuge APK…" #: platform/android/export/export.cpp -#, fuzzy msgid "" "Could not find template APK to export:\n" "%s" -msgstr "Konnte Vorlage nicht zum Export öffnen:" +msgstr "" +"Konnte keine APK-Vorlage zum Exportieren finden:\n" +"%s" #: platform/android/export/export.cpp msgid "" @@ -13615,16 +13525,18 @@ msgid "" "Please build a template with all required libraries, or uncheck the missing " "architectures in the export preset." msgstr "" +"Fehlende Bibliotheken in Exportvorlage für die ausgewählten Architekturen: " +"%s.\n" +"Es muss entweder eine Exportvorlage mit den allen benötigten Bibliotheken " +"gebaut werden oder die angegebenen Architekturen müssen abgewählt werden." #: platform/android/export/export.cpp -#, fuzzy msgid "Adding files..." -msgstr "%s hinzufügen…" +msgstr "Füge Dateien hinzu…" #: platform/android/export/export.cpp -#, fuzzy msgid "Could not export project files" -msgstr "Konnte Datei nicht schreiben:" +msgstr "Projektdateien konnten nicht exportiert werden" #: platform/android/export/export.cpp msgid "Aligning APK..." @@ -13632,7 +13544,7 @@ msgstr "Richte APK aus..." #: platform/android/export/export.cpp msgid "Could not unzip temporary unaligned APK." -msgstr "" +msgstr "Temporäres unausgerichtetes APK konnte nicht entpackt werden." #: platform/iphone/export/export.cpp platform/osx/export/export.cpp msgid "Identifier is missing." @@ -13680,45 +13592,40 @@ msgid "Could not write file:" msgstr "Konnte Datei nicht schreiben:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Could not read file:" -msgstr "Konnte Datei nicht schreiben:" +msgstr "Konnte Datei nicht lesen:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Could not read HTML shell:" -msgstr "Konnte benutzerdefinierte HTML-Shell nicht lesen:" +msgstr "Konnte HTML-Shell nicht lesen:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Could not create HTTP server directory:" -msgstr "Ordner konnte nicht erstellt werden." +msgstr "Konnte HTTP-Server-Verzeichnis nicht erstellen:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Error starting HTTP server:" -msgstr "Fehler beim Speichern der Szene." +msgstr "Fehler beim Starten des HTTP-Servers:" #: platform/osx/export/export.cpp -#, fuzzy msgid "Invalid bundle identifier:" -msgstr "Ungültiger Bezeichner:" +msgstr "Ungültiger Bundle-Bezeichner:" #: platform/osx/export/export.cpp msgid "Notarization: code signing required." -msgstr "" +msgstr "Beglaubigung: Code-Signierung wird benötigt." #: platform/osx/export/export.cpp msgid "Notarization: hardened runtime required." -msgstr "" +msgstr "Beglaubigung: Abgehärtete Ausführungsumgebung wird benötigt." #: platform/osx/export/export.cpp msgid "Notarization: Apple ID name not specified." -msgstr "" +msgstr "Beglaubigung: Apple-ID-Name nicht angegeben." #: platform/osx/export/export.cpp msgid "Notarization: Apple ID password not specified." -msgstr "" +msgstr "Beglaubigung: Apple-ID-Passwort nicht angegeben." #: platform/uwp/export/export.cpp msgid "Invalid package short name." @@ -14162,6 +14069,9 @@ msgid "" "longer has any effect.\n" "To remove this warning, disable the GIProbe's Compress property." msgstr "" +"Die Compress-Option von GIProbe gilt als veraltet wegen vorhandener Fehler " +"und hat keinen Effekt mehr.\n" +"Um diese Warnung zu deaktivieren, muss die Option deaktiviert werden." #: scene/3d/light.cpp msgid "A SpotLight with an angle wider than 90 degrees cannot cast shadows." @@ -14255,14 +14165,16 @@ msgstr "Node A und Node B müssen unterschiedliche PhysicsBody-Nodes sein" #: scene/3d/portal.cpp msgid "The RoomManager should not be a child or grandchild of a Portal." msgstr "" +"RoomManager darf kein direktes oder indirektes Unterelement von Portal sein." #: scene/3d/portal.cpp msgid "A Room should not be a child or grandchild of a Portal." -msgstr "" +msgstr "Room darf kein direktes oder indirektes Unterelement von Portal sein." #: scene/3d/portal.cpp msgid "A RoomGroup should not be a child or grandchild of a Portal." msgstr "" +"RoomGroup darf kein direktes oder indirektes Unterelement von Portal sein." #: scene/3d/remote_transform.cpp msgid "" @@ -14275,41 +14187,85 @@ msgstr "" #: scene/3d/room.cpp msgid "A Room cannot have another Room as a child or grandchild." msgstr "" +"Room darf kein direktes oder indirektes Unterelement von einem anderen Room " +"sein." #: scene/3d/room.cpp msgid "The RoomManager should not be placed inside a Room." -msgstr "" +msgstr "RoomManager darf nicht in einem Room platziert werden." #: scene/3d/room.cpp msgid "A RoomGroup should not be placed inside a Room." -msgstr "" +msgstr "RoomGroup darf nicht in einem Room platziert werden." #: scene/3d/room.cpp msgid "" "Room convex hull contains a large number of planes.\n" "Consider simplifying the room bound in order to increase performance." msgstr "" +"Die konvexe Hülle von Room enthält viele Ebenen.\n" +"Es bietet sich an Room-Hülle zu vereinfachen um die Leistung zu steigern." #: scene/3d/room_group.cpp msgid "The RoomManager should not be placed inside a RoomGroup." -msgstr "" +msgstr "RoomManager darf nicht in einer RoomGroup platziert werden." #: scene/3d/room_manager.cpp msgid "The RoomList has not been assigned." -msgstr "" +msgstr "RoomList wurde nicht zugewiesen." #: scene/3d/room_manager.cpp msgid "The RoomList node should be a Spatial (or derived from Spatial)." msgstr "" +"Das RoomList-Node muss ein Spatial (oder ein von Spatial abgeleitetes Node) " +"sein." #: scene/3d/room_manager.cpp msgid "" "Portal Depth Limit is set to Zero.\n" "Only the Room that the Camera is in will render." msgstr "" +"Die Portal-Tiefengrenze liegt bei Null.\n" +"Nur der Raum, der die Kamera enthält, wird gerendert werden." #: scene/3d/room_manager.cpp msgid "There should only be one RoomManager in the SceneTree." +msgstr "Es darf nur ein RoomManager im Szenenbaum vorhanden sein." + +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." msgstr "" #: scene/3d/soft_body.cpp @@ -14378,7 +14334,7 @@ msgstr "Animation nicht gefunden: ‚%s‘" #: scene/animation/animation_player.cpp msgid "Anim Apply Reset" -msgstr "" +msgstr "Anim Reset anwenden" #: scene/animation/animation_tree.cpp msgid "In node '%s', invalid animation: '%s'." @@ -14562,25 +14518,30 @@ msgid "Invalid comparison function for that type." msgstr "Ungültige Vergleichsfunktion für diesen Typ." #: servers/visual/shader_language.cpp -#, fuzzy msgid "Varying may not be assigned in the '%s' function." -msgstr "Varyings können nur in Vertex-Funktion zugewiesen werden." +msgstr "Varyings dürfen nicht in Funktion ‚%s‘ zugewiesen werden." #: servers/visual/shader_language.cpp msgid "" "Varyings which assigned in 'vertex' function may not be reassigned in " "'fragment' or 'light'." msgstr "" +"Varyings, welche in der ‚vertex‘-Funktion zugewiesen wurden, können nicht " +"erneut in der ‚fragment‘- oder ‚light‘-Funktion zugewiesen werden." #: servers/visual/shader_language.cpp msgid "" "Varyings which assigned in 'fragment' function may not be reassigned in " "'vertex' or 'light'." msgstr "" +"Varyings, welche in der ‚fragment‘-Funktion zugewiesen wurden, können nicht " +"erneut in der ‚vertex‘- oder ‚light‘-Funktion zugewiesen werden." #: servers/visual/shader_language.cpp msgid "Fragment-stage varying could not been accessed in custom function!" msgstr "" +"Varying aus Fragment-Phase konnte nicht in gesonderter Funktion abgerufen " +"werden!" #: servers/visual/shader_language.cpp msgid "Assignment to function." @@ -15780,9 +15741,6 @@ msgstr "Konstanten können nicht verändert werden." #~ msgid "I see..." #~ msgstr "Verstehe..." -#~ msgid "Can't open '%s'." -#~ msgstr "‚%s‘ kann nicht geöffnet werden." - #~ msgid "Ugh" #~ msgstr "Ähm" diff --git a/editor/translations/editor.pot b/editor/translations/editor.pot index bbd0bf9a6a..0f3b125484 100644 --- a/editor/translations/editor.pot +++ b/editor/translations/editor.pot @@ -331,6 +331,7 @@ msgstr "" msgid "Remove Anim Track" msgstr "" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "" @@ -355,10 +356,25 @@ msgstr "" msgid "Anim Insert" msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "animation" +msgstr "" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "property '%s'" +msgstr "" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "" @@ -913,7 +929,7 @@ msgstr "" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2204,6 +2220,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -2983,10 +3010,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "" @@ -3591,6 +3614,14 @@ msgid "Download from:" msgstr "" #: editor/export_template_manager.cpp +msgid "Open in Web Browser" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8268,6 +8299,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Remove All Color Items" msgstr "" @@ -8292,6 +8329,12 @@ msgid "Remove All StyleBox Items" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Add Color Item" msgstr "" @@ -11699,6 +11742,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13293,6 +13344,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/el.po b/editor/translations/el.po index b26b28b5d4..93b5941f64 100644 --- a/editor/translations/el.po +++ b/editor/translations/el.po @@ -350,6 +350,7 @@ msgstr "Αλλαγή λειτουÏγίας επανάληψης κίνησης" msgid "Remove Anim Track" msgstr "ΑφαίÏεση ÎšÎ¿Î¼Î¼Î±Ï„Î¹Î¿Ï ÎšÎ¯Î½Î·ÏƒÎ·Ï‚" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "ΔημιουÏγία νÎου ÎºÎ¿Î¼Î¼Î±Ï„Î¹Î¿Ï Î³Î¹Î± %s και εισαγωγή κλειδιοÏ;" @@ -374,10 +375,28 @@ msgstr "ΔημιουÏγία" msgid "Anim Insert" msgstr "Εισαγωγή Κίνησης" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "ΑδÏνατο το άνοιγμα του '%s'." + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Κίνηση" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "Ένα AnimationPlayer δεν μποÏεί να κινήσει τον εαυτό του." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "Η ιδιότητα '%s' δεν υπάÏχει." + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "ΔημιουÏγία & Εισαγωγή Κίνησης" @@ -960,7 +979,7 @@ msgstr "ΔημιουÏγία νÎου %s" msgid "No results for \"%s\"." msgstr "ΚανÎνα αποτÎλεσμα για \"%s\"." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2312,6 +2331,17 @@ msgid "New Window" msgstr "ÎÎο ΠαÏάθυÏο" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "ΠεÏιστÏÎφεται όταν το παÏάθυÏο του επεξεÏγαστή επαναχÏωματίζεται." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Οι εισαγμÎνοι πόÏοι δεν μποÏοÏν να αποθηκευτοÏν." @@ -3190,10 +3220,6 @@ msgid "Save & Restart" msgstr "Αποθήκευση & Επανεκκίνηση" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "ΠεÏιστÏÎφεται όταν το παÏάθυÏο του επεξεÏγαστή επαναχÏωματίζεται." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "Συνεχόμενη ΑνανÎωση" @@ -3859,6 +3885,16 @@ msgid "Download from:" msgstr "Σφάλμα λήψης" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "ΕκτÎλεση στον πεÏιηγητή" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "ΑντιγÏαφή σφάλματος" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8759,6 +8795,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "ΑφαίÏεση όλων των στοιχείων" @@ -8789,6 +8831,12 @@ msgid "Remove All StyleBox Items" msgstr "ΑφαίÏεση όλων των στοιχείων" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Î Ïοσθήκη στοιχείων κλάσης" @@ -12470,6 +12518,16 @@ msgstr "Αλλαγή Ύψους Σχήματος ΚυλίνδÏου" msgid "Change Ray Shape Length" msgstr "Αλλαγή μήκους ακτίνας" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "ΟÏισμός θÎσης σημείου καμπÏλης" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "ΟÏισμός θÎσης σημείου καμπÏλης" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "Αλλαγή Ακτίνας ΚυλίνδÏου" @@ -14265,6 +14323,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "Το σώμα αυτό δε θα ληφθεί υπόψιν μÎχÏι να οÏίσετε Îνα πλÎγμα (mesh)." @@ -15708,9 +15802,6 @@ msgstr "Οι σταθεÏÎÏ‚ δεν μποÏοÏν να Ï„ÏÎ¿Ï€Î¿Ï€Î¿Î¹Î·Î¸Î¿Ï #~ msgid "I see..." #~ msgstr "Εντάξει..." -#~ msgid "Can't open '%s'." -#~ msgstr "ΑδÏνατο το άνοιγμα του '%s'." - #~ msgid "Ugh" #~ msgstr "α..." diff --git a/editor/translations/eo.po b/editor/translations/eo.po index 51c69266cb..9f8c869bee 100644 --- a/editor/translations/eo.po +++ b/editor/translations/eo.po @@ -346,6 +346,7 @@ msgstr "Aliigi Animadon Iteracion Modon" msgid "Remove Anim Track" msgstr "Forigi animacian trakon" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Krei NOVAN trakon por %s kaj enmeti Ålosilon?" @@ -370,10 +371,27 @@ msgstr "Krei" msgid "Anim Insert" msgstr "Animado Enmetu" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animacio" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "AnimationPlayer ne povas animi si mem, nur aliajn ludantojn." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "Atributo" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Animado Krei & Enmeti" @@ -949,7 +967,7 @@ msgstr "Kreu novan %s" msgid "No results for \"%s\"." msgstr "Ne rezultoj por \"%s\"." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2299,6 +2317,17 @@ msgid "New Window" msgstr "Nova Fenestro" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "Rotacius kiam la fenestron de la redaktilo redesegniÄi." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Enportitaj risurcoj ne povas konservi." @@ -3169,10 +3198,6 @@ msgid "Save & Restart" msgstr "Konservi kaj rekomenci" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "Rotacius kiam la fenestron de la redaktilo redesegniÄi." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "Äœisdatigi kontinue" @@ -3827,6 +3852,16 @@ msgid "Download from:" msgstr "ElÅuta eraro" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Ruli en foliumilo" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "Kopii eraro" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8655,6 +8690,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Forigi elementon" @@ -8685,6 +8726,12 @@ msgid "Remove All StyleBox Items" msgstr "Forigi elementon" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Aldoni al favoritaj" @@ -12208,6 +12255,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13839,6 +13894,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/es.po b/editor/translations/es.po index 6f0b9cd14f..eef4affde3 100644 --- a/editor/translations/es.po +++ b/editor/translations/es.po @@ -68,11 +68,12 @@ # hiking <joaquinfc@protonmail.com>, 2021. # pabloggomez <pgg2733@gmail.com>, 2021. # Erick Figueroa <querecuto@hotmail.com>, 2021. +# jonagamerpro1234 ss <js398704@gmail.com>, 2021. msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2021-07-29 02:33+0000\n" +"PO-Revision-Date: 2021-08-12 14:48+0000\n" "Last-Translator: Javier Ocampos <xavier.ocampos@gmail.com>\n" "Language-Team: Spanish <https://hosted.weblate.org/projects/godot-engine/" "godot/es/>\n" @@ -81,7 +82,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.7.2-dev\n" +"X-Generator: Weblate 4.8-dev\n" #: core/math/expression.cpp modules/gdscript/gdscript_functions.cpp #: modules/visual_script/visual_script_builtin_funcs.cpp @@ -405,6 +406,7 @@ msgstr "Cambiar Modo Loop de Animación" msgid "Remove Anim Track" msgstr "Eliminar Pista de Animación" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "¿Crear nueva pista para %s e insertar clave?" @@ -429,10 +431,28 @@ msgstr "Crear" msgid "Anim Insert" msgstr "Insertar Animación" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "No se puede abrir '%s'." + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animación" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "Un AnimationPlayer no puede animarse a sà mismo, solo a otros players." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "No existe la propiedad '%s'." + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Crear e Insertar Animación" @@ -645,9 +665,8 @@ msgid "Go to Previous Step" msgstr "Ir al Paso Anterior" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Apply Reset" -msgstr "Resetear" +msgstr "Aplicar Restablecer" #: editor/animation_track_editor.cpp msgid "Optimize Animation" @@ -666,9 +685,8 @@ msgid "Use Bezier Curves" msgstr "Usar Curvas Bezier" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Create RESET Track(s)" -msgstr "Pegar Pistas" +msgstr "Crear pista(s) RESET" #: editor/animation_track_editor.cpp msgid "Anim. Optimizer" @@ -777,7 +795,7 @@ msgstr "%d coincidencias." #: editor/code_editor.cpp editor/find_in_files.cpp msgid "Match Case" -msgstr "Coincidir Mayús./Minús." +msgstr "Distinguir mayúsculas y minúsculas" #: editor/code_editor.cpp editor/find_in_files.cpp msgid "Whole Words" @@ -995,9 +1013,8 @@ msgid "Edit..." msgstr "Editar..." #: editor/connections_dialog.cpp -#, fuzzy msgid "Go to Method" -msgstr "Ir Al Método" +msgstr "Ir al Método" #: editor/create_dialog.cpp msgid "Change %s Type" @@ -1015,9 +1032,9 @@ msgstr "Crear Nuevo %s" msgid "No results for \"%s\"." msgstr "No hay resultados para \"%s\"." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." -msgstr "" +msgstr "No hay descripción disponible para %s." #: editor/create_dialog.cpp editor/editor_file_dialog.cpp #: editor/filesystem_dock.cpp @@ -1117,18 +1134,16 @@ msgid "Owners Of:" msgstr "Propietarios De:" #: editor/dependency_editor.cpp -#, fuzzy msgid "" "Remove the selected files from the project? (Cannot be undone.)\n" "Depending on your filesystem configuration, the files will either be moved " "to the system trash or deleted permanently." msgstr "" -"¿Eliminar los archivos seleccionados del proyecto? (irreversible)\n" -"Puedes encontrar los archivos eliminados en la papelera de reciclaje del " -"sistema para restaurarlos." +"¿Eliminar los archivos seleccionados del proyecto? (No se puede deshacer).\n" +"Dependiendo de la configuración de su sistema de archivos, los archivos se " +"moverán a la papelera del sistema o se eliminarán permanentemente." #: editor/dependency_editor.cpp -#, fuzzy msgid "" "The files being removed are required by other resources in order for them to " "work.\n" @@ -1136,11 +1151,10 @@ msgid "" "Depending on your filesystem configuration, the files will either be moved " "to the system trash or deleted permanently." msgstr "" -"Otros recursos necesitan los archivos que estás intentando quitar para " -"funcionar.\n" -"¿Eliminarlos de todos modos? (irreversible)\n" -"Puedes encontrar los archivos eliminados en la papelera de reciclaje del " -"sistema para restaurarlos." +"Otros recursos requieren los archivos que se eliminan para que funcionen.\n" +"¿Eliminarlos de todos modos? (No se puede deshacer).\n" +"Dependiendo de la configuración de su sistema de archivos, los archivos se " +"moverán a la papelera del sistema o se eliminarán permanentemente." #: editor/dependency_editor.cpp msgid "Cannot remove:" @@ -1172,7 +1186,7 @@ msgstr "¡Errores al cargar!" #: editor/dependency_editor.cpp msgid "Permanently delete %d item(s)? (No undo!)" -msgstr "¿Eliminar permanentemente %d Ãtem(s)? (¡No se puede deshacer!)" +msgstr "¿Eliminar permanentemente %d elemento(s)? (¡No se puede deshacer!)" #: editor/dependency_editor.cpp msgid "Show Dependencies" @@ -1310,41 +1324,39 @@ msgid "Licenses" msgstr "Licencias" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Error opening asset file for \"%s\" (not in ZIP format)." -msgstr "Error al abrir el archivo del paquete (no está en formato ZIP)." +msgstr "" +"Error al abrir el archivo de assets para \"%s\" (no se encuentra en formato " +"ZIP)." #: editor/editor_asset_installer.cpp -#, fuzzy msgid "%s (already exists)" -msgstr "%s (Ya existe)" +msgstr "%s (ya existe)" #: editor/editor_asset_installer.cpp msgid "Contents of asset \"%s\" - %d file(s) conflict with your project:" -msgstr "" +msgstr "El contenido del asset \"%s\" - %d entra en conflicto con el proyecto:" #: editor/editor_asset_installer.cpp msgid "Contents of asset \"%s\" - No files conflict with your project:" msgstr "" +"Contenido del asset \"%s\" - No hay archivos en conflicto con el proyecto:" #: editor/editor_asset_installer.cpp msgid "Uncompressing Assets" -msgstr "Descomprimiendo Assets" +msgstr "Descomprimir Assets" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "The following files failed extraction from asset \"%s\":" -msgstr "Los siguientes archivos no se pudieron extraer del paquete:" +msgstr "Ha fallado la extracción de los siguientes archivos del asset \"%s\":" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "(and %s more files)" -msgstr "Y %d archivos más." +msgstr "(y %s archivos más)" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Asset \"%s\" installed successfully!" -msgstr "¡Paquete instalado con éxito!" +msgstr "¡El asset \"%s\" ha sido instalado con éxito!" #: editor/editor_asset_installer.cpp #: editor/plugins/asset_library_editor_plugin.cpp @@ -1356,9 +1368,8 @@ msgid "Install" msgstr "Instalar" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Asset Installer" -msgstr "Instalador de paquetes" +msgstr "Instalador de Assets" #: editor/editor_audio_buses.cpp msgid "Speakers" @@ -1421,7 +1432,6 @@ msgid "Bypass" msgstr "Omitir" #: editor/editor_audio_buses.cpp -#, fuzzy msgid "Bus Options" msgstr "Opciones de Bus" @@ -1589,13 +1599,12 @@ msgid "Can't add autoload:" msgstr "No se puede añadir un autoload:" #: editor/editor_autoload_settings.cpp -#, fuzzy msgid "%s is an invalid path. File does not exist." msgstr "El archivo no existe." #: editor/editor_autoload_settings.cpp msgid "%s is an invalid path. Not in resource path (res://)." -msgstr "" +msgstr "%s es una ruta inválida. No está en la ruta del recurso (res://)." #: editor/editor_autoload_settings.cpp msgid "Add AutoLoad" @@ -1619,7 +1628,6 @@ msgid "Name" msgstr "Nombre" #: editor/editor_autoload_settings.cpp -#, fuzzy msgid "Global Variable" msgstr "Variable" @@ -1778,7 +1786,7 @@ msgstr "Editor de Script" #: editor/editor_feature_profile.cpp msgid "Asset Library" -msgstr "Biblioteca de Assets" +msgstr "LibrerÃa de Assets" #: editor/editor_feature_profile.cpp msgid "Scene Tree Editing" @@ -1798,48 +1806,54 @@ msgstr "Importación" #: editor/editor_feature_profile.cpp msgid "Allows to view and edit 3D scenes." -msgstr "" +msgstr "Permite ver y editar escenas 3D." #: editor/editor_feature_profile.cpp msgid "Allows to edit scripts using the integrated script editor." -msgstr "" +msgstr "Permite editar scripts utilizando el editor de scripts integrado." #: editor/editor_feature_profile.cpp msgid "Provides built-in access to the Asset Library." -msgstr "" +msgstr "Proporciona acceso integrado a la LibrerÃa de Assets." #: editor/editor_feature_profile.cpp msgid "Allows editing the node hierarchy in the Scene dock." -msgstr "" +msgstr "Permite editar la jerarquÃa de nodos en el dock de Escena." #: editor/editor_feature_profile.cpp msgid "" "Allows to work with signals and groups of the node selected in the Scene " "dock." msgstr "" +"Permite trabajar con señales y grupos del nodo seleccionado en el dock de " +"Escena." #: editor/editor_feature_profile.cpp msgid "Allows to browse the local file system via a dedicated dock." msgstr "" +"Permite navegar por el sistema de archivos local a través de un dock " +"dedicado." #: editor/editor_feature_profile.cpp msgid "" "Allows to configure import settings for individual assets. Requires the " "FileSystem dock to function." msgstr "" +"Permite configurar los ajustes de importación para assets individuales. " +"Requiere el dock FileSystem para funcionar." #: editor/editor_feature_profile.cpp -#, fuzzy msgid "(current)" -msgstr "(Actual)" +msgstr "(actual)" #: editor/editor_feature_profile.cpp msgid "(none)" -msgstr "" +msgstr "(ninguno)" #: editor/editor_feature_profile.cpp msgid "Remove currently selected profile, '%s'? Cannot be undone." msgstr "" +"¿Eliminar el perfil seleccionado en este momento, '%s'? No se puede deshacer." #: editor/editor_feature_profile.cpp msgid "Profile must be a valid filename and must not contain '.'" @@ -1871,19 +1885,16 @@ msgid "Enable Contextual Editor" msgstr "Activar el Editor Contextual" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Class Properties:" -msgstr "Propiedades:" +msgstr "Propiedades de Clase:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Main Features:" -msgstr "CaracterÃsticas" +msgstr "CaracterÃsticas principales:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Nodes and Classes:" -msgstr "Clases Activadas:" +msgstr "Clases y Nodos:" #: editor/editor_feature_profile.cpp msgid "File '%s' format is invalid, import aborted." @@ -1903,7 +1914,6 @@ msgid "Error saving profile to path: '%s'." msgstr "Error al guardar el perfil en la ruta: '%s'." #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Reset to Default" msgstr "Restablecer Valores por Defecto" @@ -1912,14 +1922,12 @@ msgid "Current Profile:" msgstr "Perfil Actual:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Create Profile" -msgstr "Borrar Perfil" +msgstr "Crear perfil" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Remove Profile" -msgstr "Eliminar Tile" +msgstr "Eliminar perfil" #: editor/editor_feature_profile.cpp msgid "Available Profiles:" @@ -1939,18 +1947,17 @@ msgid "Export" msgstr "Exportar" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Configure Selected Profile:" -msgstr "Perfil Actual:" +msgstr "Configurar el perfil seleccionado:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Extra Options:" -msgstr "Opciones de textura" +msgstr "Opciones adicionales:" #: editor/editor_feature_profile.cpp msgid "Create or import a profile to edit available classes and properties." msgstr "" +"Crea o importa un perfil para editar las clases y propiedades disponibles." #: editor/editor_feature_profile.cpp msgid "New profile name:" @@ -1977,9 +1984,8 @@ msgid "Select Current Folder" msgstr "Seleccionar Carpeta Actual" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp -#, fuzzy msgid "File exists, overwrite?" -msgstr "El archivo ya existe ¿Quieres sobreescribirlo?" +msgstr "El archivo existe, ¿sobrescribirlo?" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp msgid "Select This Folder" @@ -2105,11 +2111,11 @@ msgstr "Mostrar/Ocultar archivos ocultos." #: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp msgid "View items as a grid of thumbnails." -msgstr "Ver Ãtems como una cuadrÃcula de miniaturas." +msgstr "Ver elementos como una cuadrÃcula de miniaturas." #: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp msgid "View items as a list." -msgstr "Ver Ãtems como una lista." +msgstr "Ver elementos como una lista." #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp msgid "Directories & Files:" @@ -2138,7 +2144,7 @@ msgstr "" #: editor/editor_file_system.cpp msgid "(Re)Importing Assets" -msgstr "(Re)Importando Assets" +msgstr "(Re)Importación de Assets" #: editor/editor_help.cpp editor/plugins/spatial_editor_plugin.cpp msgid "Top" @@ -2183,7 +2189,7 @@ msgstr "Métodos" #: editor/editor_help.cpp msgid "Theme Properties" -msgstr "Propiedades de Temas" +msgstr "Propiedades del Theme" #: editor/editor_help.cpp msgid "Enumerations" @@ -2260,7 +2266,7 @@ msgstr "Solo Propiedades" #: editor/editor_help_search.cpp msgid "Theme Properties Only" -msgstr "Solo Propiedades del Tema" +msgstr "Solo Propiedades del Theme" #: editor/editor_help_search.cpp msgid "Member Type" @@ -2288,7 +2294,7 @@ msgstr "Propiedad" #: editor/editor_help_search.cpp msgid "Theme Property" -msgstr "Propiedades del Tema" +msgstr "Propiedades del Theme" #: editor/editor_inspector.cpp editor/project_settings_editor.cpp msgid "Property:" @@ -2372,6 +2378,17 @@ msgid "New Window" msgstr "Nueva Ventana" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "Gira cuando la ventana del editor se redibuja." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Los recursos importados no se pueden guardar." @@ -2604,13 +2621,16 @@ msgid "" "The current scene has no root node, but %d modified external resource(s) " "were saved anyway." msgstr "" +"La escena actual no tiene un nodo raÃz, pero %d recurso(s) externo(s) " +"modificado(s) fueron guardados igualmente." #: editor/editor_node.cpp -#, fuzzy msgid "" "A root node is required to save the scene. You can add a root node using the " "Scene tree dock." -msgstr "Se necesita un nodo raÃz para guardar la escena." +msgstr "" +"Se requiere un nodo raÃz para guardar la escena. Puede agregar un nodo raÃz " +"utilizando el muelle de árbol de escenas." #: editor/editor_node.cpp msgid "Save Scene As..." @@ -2937,7 +2957,7 @@ msgstr "Convertir a..." #: editor/editor_node.cpp msgid "MeshLibrary..." -msgstr "MeshLibrary..." +msgstr "Biblioteca de malla..." #: editor/editor_node.cpp msgid "TileSet..." @@ -2999,9 +3019,8 @@ msgid "Orphan Resource Explorer..." msgstr "Explorador de Recursos Huérfanos..." #: editor/editor_node.cpp -#, fuzzy msgid "Reload Current Project" -msgstr "Renombrar Proyecto" +msgstr "Recargar proyecto actual" #: editor/editor_node.cpp msgid "Quit to Project List" @@ -3163,22 +3182,20 @@ msgid "Help" msgstr "Ayuda" #: editor/editor_node.cpp -#, fuzzy msgid "Online Documentation" -msgstr "Abrir Documentación" +msgstr "Documentación en lÃnea" #: editor/editor_node.cpp msgid "Questions & Answers" -msgstr "" +msgstr "Preguntas & Respuestas" #: editor/editor_node.cpp msgid "Report a Bug" msgstr "Reportar un Bug" #: editor/editor_node.cpp -#, fuzzy msgid "Suggest a Feature" -msgstr "Establecer valor" +msgstr "Sugerir una caracterÃstica" #: editor/editor_node.cpp msgid "Send Docs Feedback" @@ -3189,9 +3206,8 @@ msgid "Community" msgstr "Comunidad" #: editor/editor_node.cpp -#, fuzzy msgid "About Godot" -msgstr "Acerca de" +msgstr "Sobre Godot" #: editor/editor_node.cpp msgid "Support Godot Development" @@ -3243,10 +3259,6 @@ msgid "Save & Restart" msgstr "Guardar y Reiniciar" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "Gira cuando la ventana del editor se redibuja." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "Actualizar Continuamente" @@ -3289,14 +3301,12 @@ msgid "Manage Templates" msgstr "Administrar Plantillas" #: editor/editor_node.cpp -#, fuzzy msgid "Install from file" -msgstr "Instalar Desde Archivo" +msgstr "Instalar desde archivo" #: editor/editor_node.cpp -#, fuzzy msgid "Select android sources file" -msgstr "Selecciona una Malla de Origen:" +msgstr "Seleccionar el archivo fuente de Android" #: editor/editor_node.cpp msgid "" @@ -3380,9 +3390,8 @@ msgid "Select" msgstr "Seleccionar" #: editor/editor_node.cpp -#, fuzzy msgid "Select Current" -msgstr "Seleccionar Carpeta Actual" +msgstr "Seleccionar actual" #: editor/editor_node.cpp msgid "Open 2D Editor" @@ -3398,7 +3407,7 @@ msgstr "Abrir Editor de Script" #: editor/editor_node.cpp editor/project_manager.cpp msgid "Open Asset Library" -msgstr "Abrir Biblioteca de Assets" +msgstr "Abrir LibrerÃa de Assets" #: editor/editor_node.cpp msgid "Open the next Editor" @@ -3417,9 +3426,8 @@ msgid "No sub-resources found." msgstr "No se encontró ningún sub-recurso." #: editor/editor_path.cpp -#, fuzzy msgid "Open a list of sub-resources." -msgstr "No se encontró ningún sub-recurso." +msgstr "Abra una lista de sub-recursos." #: editor/editor_plugin.cpp msgid "Creating Mesh Previews" @@ -3446,14 +3454,12 @@ msgid "Update" msgstr "Actualizar" #: editor/editor_plugin_settings.cpp -#, fuzzy msgid "Version" -msgstr "Versión:" +msgstr "Versión" #: editor/editor_plugin_settings.cpp -#, fuzzy msgid "Author" -msgstr "Autores" +msgstr "Autor" #: editor/editor_plugin_settings.cpp #: editor/plugins/version_control_editor_plugin.cpp @@ -3466,14 +3472,12 @@ msgid "Measure:" msgstr "Medida:" #: editor/editor_profiler.cpp -#, fuzzy msgid "Frame Time (ms)" -msgstr "Duración de Fotogramas (seg)" +msgstr "Duración de Fotogramas (ms)" #: editor/editor_profiler.cpp -#, fuzzy msgid "Average Time (ms)" -msgstr "Tiempo Promedio (seg)" +msgstr "Tiempo Promedio (ms)" #: editor/editor_profiler.cpp msgid "Frame %" @@ -3500,6 +3504,12 @@ msgid "" "functions called by that function.\n" "Use this to find individual functions to optimize." msgstr "" +"Inclusivo: Incluye el tiempo de otras funciones llamadas por esta función.\n" +"UtilÃzalo para detectar cuellos de botella.\n" +"\n" +"Propio: Sólo contabiliza el tiempo empleado en la propia función, no en " +"otras funciones llamadas por esa función.\n" +"UtilÃzalo para buscar funciones individuales que optimizar." #: editor/editor_profiler.cpp msgid "Frame #:" @@ -3581,7 +3591,7 @@ msgstr "Página: " #: editor/editor_properties_array_dict.cpp #: editor/plugins/theme_editor_plugin.cpp msgid "Remove Item" -msgstr "Eliminar Ãtem" +msgstr "Eliminar Elemento" #: editor/editor_properties_array_dict.cpp msgid "New Key:" @@ -3622,7 +3632,6 @@ msgid "Paste" msgstr "Pegar" #: editor/editor_resource_picker.cpp editor/property_editor.cpp -#, fuzzy msgid "Convert to %s" msgstr "Convertir a %s" @@ -3674,11 +3683,10 @@ msgid "Did you forget the '_run' method?" msgstr "Te olvidaste del método '_run'?" #: editor/editor_spin_slider.cpp -#, fuzzy msgid "Hold %s to round to integers. Hold Shift for more precise changes." msgstr "" -"Mantén pulsado Ctrl para redondear a enteros. Mantén pulsado Shift para " -"cambios más precisos." +"Mantenga presionado %s para redondear a números enteros. Mantenga presionada " +"la tecla Mayús para cambios más precisos." #: editor/editor_sub_scene.cpp msgid "Select Node(s) to Import" @@ -3698,49 +3706,43 @@ msgstr "Importar Desde Nodo:" #: editor/export_template_manager.cpp msgid "Open the folder containing these templates." -msgstr "" +msgstr "Abra la carpeta que contiene estas plantillas." #: editor/export_template_manager.cpp msgid "Uninstall these templates." -msgstr "" +msgstr "Desinstalar estas plantillas." #: editor/export_template_manager.cpp -#, fuzzy msgid "There are no mirrors available." -msgstr "No hay ningún archivo `%s'." +msgstr "No hay espejos disponibles." #: editor/export_template_manager.cpp -#, fuzzy msgid "Retrieving the mirror list..." -msgstr "Obteniendo mirrors, por favor espera..." +msgstr "Recuperar la lista de espejos..." #: editor/export_template_manager.cpp msgid "Starting the download..." -msgstr "" +msgstr "Comenzando la descarga..." #: editor/export_template_manager.cpp msgid "Error requesting URL:" msgstr "Error al solicitar la URL:" #: editor/export_template_manager.cpp -#, fuzzy msgid "Connecting to the mirror..." -msgstr "Conectando con Mirror...." +msgstr "Conectando con el espejo..." #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't resolve the requested address." -msgstr "No se ha podido resolver el nombre de dominio:" +msgstr "No se puede resolver la dirección solicitada." #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't connect to the mirror." -msgstr "No se puede conectar al host:" +msgstr "No se puede conectar al espejo." #: editor/export_template_manager.cpp -#, fuzzy msgid "No response from the mirror." -msgstr "No hay respuesta desde el host:" +msgstr "No hay respuesta del espejo." #: editor/export_template_manager.cpp #: editor/plugins/asset_library_editor_plugin.cpp @@ -3748,18 +3750,16 @@ msgid "Request failed." msgstr "Petición fallida." #: editor/export_template_manager.cpp -#, fuzzy msgid "Request ended up in a redirect loop." -msgstr "Petición fallida, demasiadas redirecciones" +msgstr "La solicitud terminó en un bucle de redirección." #: editor/export_template_manager.cpp -#, fuzzy msgid "Request failed:" -msgstr "Petición fallida." +msgstr "Petición fallida:" #: editor/export_template_manager.cpp msgid "Download complete; extracting templates..." -msgstr "" +msgstr "Descarga completa; extracción de plantillas..." #: editor/export_template_manager.cpp msgid "Cannot remove temporary file:" @@ -3775,18 +3775,17 @@ msgstr "" #: editor/export_template_manager.cpp msgid "Error getting the list of mirrors." -msgstr "Error al obtener la lista de mirrors." +msgstr "Error al obtener la lista de espejos." #: editor/export_template_manager.cpp -#, fuzzy msgid "Error parsing JSON with the list of mirrors. Please report this issue!" msgstr "" -"Error al analizar el JSON de la lista de mirrors. ¡Por favor, informa de " -"este problema!" +"Error al analizar el JSON con la lista de espejos ¡Por favor, reporta este " +"problema!" #: editor/export_template_manager.cpp msgid "Best available mirror" -msgstr "" +msgstr "El mejor espejo disponible" #: editor/export_template_manager.cpp msgid "" @@ -3839,22 +3838,18 @@ msgid "SSL Handshake Error" msgstr "Error de Negociación SSL" #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't open the export templates file." -msgstr "No se puede abir el zip de plantillas de exportación." +msgstr "No se puede abrir el archivo de plantillas de exportación." #: editor/export_template_manager.cpp -#, fuzzy msgid "Invalid version.txt format inside the export templates file: %s." msgstr "Formato de version.txt inválido dentro de plantillas: %s." #: editor/export_template_manager.cpp -#, fuzzy msgid "No version.txt found inside the export templates file." msgstr "No se ha encontrado el archivo version.txt dentro de las plantillas." #: editor/export_template_manager.cpp -#, fuzzy msgid "Error creating path for extracting templates:" msgstr "Error al crear ruta para las plantillas:" @@ -3867,9 +3862,8 @@ msgid "Importing:" msgstr "Importando:" #: editor/export_template_manager.cpp -#, fuzzy msgid "Remove templates for the version '%s'?" -msgstr "¿Eliminar plantilla versión '%s'?" +msgstr "¿Eliminar plantilla de la versión '%s'?" #: editor/export_template_manager.cpp msgid "Uncompressing Android Build Sources" @@ -3886,43 +3880,57 @@ msgstr "Versión Actual:" #: editor/export_template_manager.cpp msgid "Export templates are missing. Download them or install from a file." msgstr "" +"Faltan las plantillas de exportación. Puede descargarlas o instalarlas desde " +"un archivo." #: editor/export_template_manager.cpp msgid "Export templates are installed and ready to be used." msgstr "" +"Las plantillas de exportación están instaladas y listas para ser utilizadas." #: editor/export_template_manager.cpp -#, fuzzy msgid "Open Folder" -msgstr "Abrir Archivo" +msgstr "Abrir carpeta" #: editor/export_template_manager.cpp msgid "Open the folder containing installed templates for the current version." msgstr "" +"Abre la carpeta que contiene las plantillas instaladas para la versión " +"actual." #: editor/export_template_manager.cpp msgid "Uninstall" msgstr "Desinstalar" #: editor/export_template_manager.cpp -#, fuzzy msgid "Uninstall templates for the current version." -msgstr "Valor inicial para el contador" +msgstr "Desinstalar las plantillas de la versión actual." #: editor/export_template_manager.cpp -#, fuzzy msgid "Download from:" -msgstr "Error de descarga" +msgstr "Descargar desde:" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Ejecutar en Navegador" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "Copiar Error" #: editor/export_template_manager.cpp msgid "Download and Install" -msgstr "" +msgstr "Descargar e instalar" #: editor/export_template_manager.cpp msgid "" "Download and install templates for the current version from the best " "possible mirror." msgstr "" +"Descarga e instala las plantillas de la versión actual desde el mejor espejo " +"posible." #: editor/export_template_manager.cpp msgid "Official export templates aren't available for development builds." @@ -3931,14 +3939,12 @@ msgstr "" "versiones de desarrollo." #: editor/export_template_manager.cpp -#, fuzzy msgid "Install from File" -msgstr "Instalar Desde Archivo" +msgstr "Instalar desde un archivo" #: editor/export_template_manager.cpp -#, fuzzy msgid "Install templates from a local file." -msgstr "Importar plantillas desde un archivo ZIP" +msgstr "Instalar plantillas desde un archivo local." #: editor/export_template_manager.cpp editor/find_in_files.cpp #: editor/progress_dialog.cpp scene/gui/dialogs.cpp @@ -3946,23 +3952,20 @@ msgid "Cancel" msgstr "Cancelar" #: editor/export_template_manager.cpp -#, fuzzy msgid "Cancel the download of the templates." -msgstr "No se puede abir el zip de plantillas de exportación." +msgstr "Cancele la descarga de las plantillas." #: editor/export_template_manager.cpp -#, fuzzy msgid "Other Installed Versions:" -msgstr "Versiones Instaladas:" +msgstr "Otras versiones instaladas:" #: editor/export_template_manager.cpp -#, fuzzy msgid "Uninstall Template" -msgstr "Desinstalar" +msgstr "Desinstalar plantilla" #: editor/export_template_manager.cpp msgid "Select Template File" -msgstr "Selecciona un Archivo de Plantilla" +msgstr "Seleccionar el archivo de la plantilla" #: editor/export_template_manager.cpp msgid "Godot Export Templates" @@ -3973,6 +3976,8 @@ msgid "" "The templates will continue to download.\n" "You may experience a short editor freeze when they finish." msgstr "" +"Las plantillas seguirán descargándose.\n" +"Es posible que experimente una breve congelación del editor cuando terminen." #: editor/filesystem_dock.cpp msgid "Favorites" @@ -4120,35 +4125,32 @@ msgid "Collapse All" msgstr "Colapsar Todo" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort files" -msgstr "Buscar archivos" +msgstr "Ordenar archivos" #: editor/filesystem_dock.cpp msgid "Sort by Name (Ascending)" -msgstr "" +msgstr "Ordenar por Nombre (Ascendente)" #: editor/filesystem_dock.cpp msgid "Sort by Name (Descending)" -msgstr "" +msgstr "Ordenar por Nombre (Descendente)" #: editor/filesystem_dock.cpp msgid "Sort by Type (Ascending)" -msgstr "" +msgstr "Ordenar por Tipo (Ascendente)" #: editor/filesystem_dock.cpp msgid "Sort by Type (Descending)" -msgstr "" +msgstr "Ordenar por Tipo (Descendente)" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort by Last Modified" -msgstr "Ultima Modificación" +msgstr "Ordenar por Última Modificación" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort by First Modified" -msgstr "Ultima Modificación" +msgstr "Ordenar por Primera Modificación" #: editor/filesystem_dock.cpp msgid "Duplicate..." @@ -4160,7 +4162,7 @@ msgstr "Renombrar..." #: editor/filesystem_dock.cpp msgid "Focus the search box" -msgstr "" +msgstr "Enfocar el cuadro de búsqueda" #: editor/filesystem_dock.cpp msgid "Previous Folder/File" @@ -4461,22 +4463,20 @@ msgstr "Cambiar el tipo de un archivo importado requiere reiniciar el editor." msgid "" "WARNING: Assets exist that use this resource, they may stop loading properly." msgstr "" -"ADVERTENCIA: Existen recursos que utilizan este recurso, pueden dejar de " -"cargar correctamente." +"ADVERTENCIA: Existen assets que utilizan este recurso, pueden dejar de " +"cargarse correctamente." #: editor/inspector_dock.cpp msgid "Failed to load resource." msgstr "Error al cargar el recurso." #: editor/inspector_dock.cpp -#, fuzzy msgid "Copy Properties" -msgstr "Propiedades" +msgstr "Copiar Propiedades" #: editor/inspector_dock.cpp -#, fuzzy msgid "Paste Properties" -msgstr "Propiedades" +msgstr "Pegar Propiedades" #: editor/inspector_dock.cpp msgid "Make Sub-Resources Unique" @@ -4501,23 +4501,20 @@ msgid "Save As..." msgstr "Guardar como..." #: editor/inspector_dock.cpp -#, fuzzy msgid "Extra resource options." -msgstr "No está en la ruta de recursos." +msgstr "Opciones de recursos extra." #: editor/inspector_dock.cpp -#, fuzzy msgid "Edit Resource from Clipboard" -msgstr "Editar Portapapeles de Recursos" +msgstr "Editar Recurso desde el Portapapeles" #: editor/inspector_dock.cpp msgid "Copy Resource" msgstr "Copiar Recurso" #: editor/inspector_dock.cpp -#, fuzzy msgid "Make Resource Built-In" -msgstr "Crear Integrado" +msgstr "Crear Recursos Integrados" #: editor/inspector_dock.cpp msgid "Go to the previous edited object in history." @@ -4532,9 +4529,8 @@ msgid "History of recently edited objects." msgstr "Historial de objetos recientemente editados." #: editor/inspector_dock.cpp -#, fuzzy msgid "Open documentation for this object." -msgstr "Abrir Documentación" +msgstr "Abrir la documentación de este objeto." #: editor/inspector_dock.cpp editor/scene_tree_dock.cpp msgid "Open Documentation" @@ -4545,9 +4541,8 @@ msgid "Filter properties" msgstr "Filtrar propiedades" #: editor/inspector_dock.cpp -#, fuzzy msgid "Manage object properties." -msgstr "Propiedades del objeto." +msgstr "Administrar propiedades de los objetos." #: editor/inspector_dock.cpp msgid "Changes may be lost!" @@ -4793,9 +4788,8 @@ msgid "Blend:" msgstr "Mezcla:" #: editor/plugins/animation_blend_tree_editor_plugin.cpp -#, fuzzy msgid "Parameter Changed:" -msgstr "Parámetro Modificado" +msgstr "Parámetro Modificado:" #: editor/plugins/animation_blend_tree_editor_plugin.cpp #: editor/plugins/animation_tree_player_editor_plugin.cpp @@ -5445,7 +5439,7 @@ msgstr "Fallo en la comprobación del hash SHA-256" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Asset Download Error:" -msgstr "Error en la descarga del asset:" +msgstr "Error de Descarga de Assets:" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Downloading (%s / %s)..." @@ -5481,7 +5475,7 @@ msgstr "Error de descarga" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Download for this asset is already in progress!" -msgstr "¡Éste asset ya está descargándose!" +msgstr "¡La descarga de este asset ya está en proceso!" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Recently Updated" @@ -5529,11 +5523,11 @@ msgstr "Todos" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Search templates, projects, and demos" -msgstr "" +msgstr "Buscar plantillas, proyectos y demostraciones" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Search assets (excluding templates, projects, and demos)" -msgstr "" +msgstr "Buscar assets (excluyendo plantillas, proyectos y demos)" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Import..." @@ -5573,11 +5567,11 @@ msgstr "Cargar..." #: editor/plugins/asset_library_editor_plugin.cpp msgid "Assets ZIP File" -msgstr "Archivo ZIP de elementos" +msgstr "Archivo ZIP de assets" #: editor/plugins/audio_stream_editor_plugin.cpp msgid "Audio Preview Play/Pause" -msgstr "" +msgstr "Previsualización de Audio Reproducir/Pausar" #: editor/plugins/baked_lightmap_editor_plugin.cpp msgid "" @@ -5838,13 +5832,13 @@ msgstr "Cambiar Anclas" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "" "Project Camera Override\n" "Overrides the running project's camera with the editor viewport camera." msgstr "" -"Reemplazar Cámara del Juego\n" -"Reemplaza la cámara del juego por la cámara del viewport del editor." +"Anulación de la Cámara del Proyecto\n" +"Anula la cámara del proyecto en ejecución por la cámara del viewport del " +"editor." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp @@ -5853,6 +5847,9 @@ msgid "" "No project instance running. Run the project from the editor to use this " "feature." msgstr "" +"Anulación de la Cámara del Proyecto\n" +"No se está ejecutando ninguna instancia del proyecto. Ejecuta el proyecto " +"desde el editor para utilizar esta función." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp @@ -5920,32 +5917,27 @@ msgstr "Modo de Selección" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Drag: Rotate selected node around pivot." -msgstr "Eliminar el nodo o transición seleccionado/a." +msgstr "Arrastrar: Girar el nodo seleccionado alrededor del pivote." #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Alt+Drag: Move selected node." -msgstr "Alt+Arrastrar: Mover" +msgstr "Alt+Arrastrar: Mover el nodo seleccionado." #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "V: Set selected node's pivot position." -msgstr "Eliminar el nodo o transición seleccionado/a." +msgstr "V: Establecer la posición de pivote del nodo seleccionado." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Alt+RMB: Show list of all nodes at position clicked, including locked." msgstr "" -"Mostrar una lista de todos los objetos en la posición en la que se ha hecho " -"clic\n" -"(igual que Alt + Clic Derecho en modo selección)." +"Alt + RMB: Muestra la lista de todos los nodos en la posición en la que se " +"hizo clic, incluido el bloqueado." #: editor/plugins/canvas_item_editor_plugin.cpp msgid "RMB: Add node at position clicked." -msgstr "" +msgstr "RMB: Añade un nodo en la posición seleccionada." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp @@ -6184,14 +6176,12 @@ msgid "Clear Pose" msgstr "Limpiar Pose" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Add Node Here" -msgstr "Añadir Nodo" +msgstr "Añadir Nodo AquÃ" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Instance Scene Here" -msgstr "Instanciar Escena(s)" +msgstr "Instanciar Escena AquÃ" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Multiply grid step by 2" @@ -6207,49 +6197,43 @@ msgstr "Vista Panorámica" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 3.125%" -msgstr "" +msgstr "Zoom al 3,125%" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 6.25%" -msgstr "" +msgstr "Zoom al 6.25%" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 12.5%" -msgstr "" +msgstr "Zoom al 12.5%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 25%" -msgstr "Alejar Zoom" +msgstr "Zoom al 25%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 50%" -msgstr "Alejar Zoom" +msgstr "Zoom al 50%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 100%" -msgstr "Alejar Zoom" +msgstr "Zoom al 100%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 200%" -msgstr "Alejar Zoom" +msgstr "Zoom al 200%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 400%" -msgstr "Alejar Zoom" +msgstr "Zoom al 400%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 800%" -msgstr "Alejar Zoom" +msgstr "Zoom al 800%" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 1600%" -msgstr "" +msgstr "Zoom al 1600%" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Add %s" @@ -6451,15 +6435,15 @@ msgstr "Degradado Editado" #: editor/plugins/item_list_editor_plugin.cpp msgid "Item %d" -msgstr "Ãtem %d" +msgstr "Elemento %d" #: editor/plugins/item_list_editor_plugin.cpp msgid "Items" -msgstr "Ãtems" +msgstr "Elementos" #: editor/plugins/item_list_editor_plugin.cpp msgid "Item List Editor" -msgstr "Editor de Lista de Ãtems" +msgstr "Editor de Lista de Elementos" #: editor/plugins/light_occluder_2d_editor_plugin.cpp msgid "Create Occluder Polygon" @@ -6496,9 +6480,8 @@ msgid "Couldn't create a single convex collision shape." msgstr "No pudo crear una única forma de colisión convexa." #: editor/plugins/mesh_instance_editor_plugin.cpp -#, fuzzy msgid "Create Simplified Convex Shape" -msgstr "Crear una Única Forma Convexa" +msgstr "Crear Forma Convexa Simplificada" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Create Single Convex Shape" @@ -6535,9 +6518,8 @@ msgid "No mesh to debug." msgstr "No hay mallas para depurar." #: editor/plugins/mesh_instance_editor_plugin.cpp -#, fuzzy msgid "Mesh has no UV in layer %d." -msgstr "El modelo no tiene UV en esta capa" +msgstr "La malla no tiene UV en la capa %d." #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "MeshInstance lacks a Mesh!" @@ -6602,9 +6584,8 @@ msgstr "" "Es la opción más rápida (pero menos precisa) para la detección de colisiones." #: editor/plugins/mesh_instance_editor_plugin.cpp -#, fuzzy msgid "Create Simplified Convex Collision Sibling" -msgstr "Crear Collider Convexo Único Hermano" +msgstr "Crear Forma de Colisión Conexa Hermana" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "" @@ -6612,20 +6593,23 @@ msgid "" "This is similar to single collision shape, but can result in a simpler " "geometry in some cases, at the cost of accuracy." msgstr "" +"Crea una forma de colisión convexa simplificada.\n" +"Esto es similar a la forma de colisión simple, pero puede resultar en una " +"geometrÃa más simple en algunos casos, a costa de la precisión." #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Create Multiple Convex Collision Siblings" msgstr "Crear Múltiples Collider Convexos Hermanos" #: editor/plugins/mesh_instance_editor_plugin.cpp -#, fuzzy msgid "" "Creates a polygon-based collision shape.\n" "This is a performance middle-ground between a single convex collision and a " "polygon-based collision." msgstr "" "Crea una forma de colisión basada en polÃgonos.\n" -"Este es un punto medio de rendimiento entre las dos opciones anteriores." +"Se trata de un punto intermedio de rendimiento entre una colisión convexa " +"única y una colisión basada en polÃgonos." #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Create Outline Mesh..." @@ -6669,7 +6653,7 @@ msgstr "Depuración del Canal UV" #: editor/plugins/mesh_library_editor_plugin.cpp msgid "Remove item %d?" -msgstr "¿Eliminar el Ãtem %d?" +msgstr "¿Eliminar el elemento %d?" #: editor/plugins/mesh_library_editor_plugin.cpp msgid "" @@ -6685,11 +6669,11 @@ msgstr "LibrerÃa de Mallas" #: editor/plugins/mesh_library_editor_plugin.cpp msgid "Add Item" -msgstr "Añadir Ãtem" +msgstr "Añadir Elemento" #: editor/plugins/mesh_library_editor_plugin.cpp msgid "Remove Selected Item" -msgstr "Eliminar Ãtem Seleccionado" +msgstr "Eliminar Elemento Seleccionado" #: editor/plugins/mesh_library_editor_plugin.cpp msgid "Import from Scene" @@ -6742,7 +6726,7 @@ msgstr "El origen de la superficie no es correcto (sin caras)." #: editor/plugins/multimesh_editor_plugin.cpp msgid "Select a Source Mesh:" -msgstr "Selecciona una Malla de Origen:" +msgstr "Seleccionar una Malla de Origen:" #: editor/plugins/multimesh_editor_plugin.cpp msgid "Select a Target Surface:" @@ -7275,24 +7259,20 @@ msgid "ResourcePreloader" msgstr "Precargador de Recursos" #: editor/plugins/room_manager_editor_plugin.cpp -#, fuzzy msgid "Flip Portals" -msgstr "Voltear Horizontalmente" +msgstr "Voltear Portales" #: editor/plugins/room_manager_editor_plugin.cpp -#, fuzzy msgid "Room Generate Points" -msgstr "Conteo de Puntos Generados:" +msgstr "Generar Puntos en la Room" #: editor/plugins/room_manager_editor_plugin.cpp -#, fuzzy msgid "Generate Points" -msgstr "Conteo de Puntos Generados:" +msgstr "Generar puntos" #: editor/plugins/room_manager_editor_plugin.cpp -#, fuzzy msgid "Flip Portal" -msgstr "Voltear Horizontalmente" +msgstr "Voltear Portal" #: editor/plugins/root_motion_editor_plugin.cpp msgid "AnimationTree has no path set to an AnimationPlayer" @@ -7324,7 +7304,7 @@ msgstr "¡Error guardando archivo!" #: editor/plugins/script_editor_plugin.cpp msgid "Error while saving theme." -msgstr "Error al guardar el tema." +msgstr "Error al guardar el theme." #: editor/plugins/script_editor_plugin.cpp msgid "Error Saving" @@ -7332,7 +7312,7 @@ msgstr "Error al Guardar" #: editor/plugins/script_editor_plugin.cpp msgid "Error importing theme." -msgstr "Error al importar el tema." +msgstr "Error al importar el theme." #: editor/plugins/script_editor_plugin.cpp msgid "Error Importing" @@ -7371,11 +7351,11 @@ msgstr "" #: editor/plugins/script_editor_plugin.cpp msgid "Import Theme" -msgstr "Importar Tema" +msgstr "Importar Theme" #: editor/plugins/script_editor_plugin.cpp msgid "Error while saving theme" -msgstr "Error al guardar el tema" +msgstr "Error al guardar el theme" #: editor/plugins/script_editor_plugin.cpp msgid "Error saving" @@ -7383,7 +7363,7 @@ msgstr "Error al guardar" #: editor/plugins/script_editor_plugin.cpp msgid "Save Theme As..." -msgstr "Guardar Tema Como..." +msgstr "Guardar Theme Como..." #: editor/plugins/script_editor_plugin.cpp msgid "%s Class Reference" @@ -7470,19 +7450,19 @@ msgstr "Siguiente en el Historial" #: editor/plugins/script_editor_plugin.cpp #: editor/plugins/theme_editor_plugin.cpp msgid "Theme" -msgstr "Tema" +msgstr "Theme" #: editor/plugins/script_editor_plugin.cpp msgid "Import Theme..." -msgstr "Importar Tema..." +msgstr "Importar Theme..." #: editor/plugins/script_editor_plugin.cpp msgid "Reload Theme" -msgstr "Recargar Tema" +msgstr "Recargar Theme" #: editor/plugins/script_editor_plugin.cpp msgid "Save Theme" -msgstr "Guardar Tema" +msgstr "Guardar Theme" #: editor/plugins/script_editor_plugin.cpp msgid "Close All" @@ -7855,20 +7835,17 @@ msgid "None" msgstr "Ninguno" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Rotate" -msgstr "Estado:" +msgstr "Rotar" #. TRANSLATORS: This refers to the movement that changes the position of an object. #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Translate" -msgstr "Trasladar:" +msgstr "Mover" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Scale" -msgstr "Escala:" +msgstr "Escala" #: editor/plugins/spatial_editor_plugin.cpp msgid "Scaling: " @@ -7891,52 +7868,44 @@ msgid "Animation Key Inserted." msgstr "Clave de animación insertada." #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Pitch:" -msgstr "Altura" +msgstr "Eje de paso:" #: editor/plugins/spatial_editor_plugin.cpp msgid "Yaw:" -msgstr "" +msgstr "Eje de guiñada:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Size:" -msgstr "Tamaño: " +msgstr "Tamaño:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Objects Drawn:" -msgstr "Objetos Dibujados" +msgstr "Objetos Dibujados:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Material Changes:" -msgstr "Cambios del Material" +msgstr "Cambios del Material:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Shader Changes:" -msgstr "Cambios del Shader" +msgstr "Cambios de sombreado:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Surface Changes:" -msgstr "Cambios de Superficie" +msgstr "Cambios de superficie:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Draw Calls:" -msgstr "Llamadas de Dibujado" +msgstr "Llamadas de Dibujado:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Vertices:" -msgstr "Vértices" +msgstr "Vértices:" #: editor/plugins/spatial_editor_plugin.cpp msgid "FPS: %d (%s ms)" -msgstr "" +msgstr "FPS: %d (%s ms)" #: editor/plugins/spatial_editor_plugin.cpp msgid "Top View." @@ -8091,9 +8060,8 @@ msgid "Freelook Slow Modifier" msgstr "Modificador de Velocidad de Vista Libre" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Toggle Camera Preview" -msgstr "Cambiar Tamaño de Cámara" +msgstr "Alternar Vista Previa de la Cámara" #: editor/plugins/spatial_editor_plugin.cpp msgid "View Rotation Locked" @@ -8115,9 +8083,8 @@ msgstr "" "No se puede utilizar como un indicador fiable del rendimiento en el juego." #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Convert Rooms" -msgstr "Convertir a %s" +msgstr "Convertir Rooms" #: editor/plugins/spatial_editor_plugin.cpp msgid "XForm Dialog" @@ -8139,7 +8106,6 @@ msgstr "" "opacas (\"x-ray\")." #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Snap Nodes to Floor" msgstr "Ajustar Nodos al Suelo" @@ -8157,7 +8123,7 @@ msgstr "Usar Snap" #: editor/plugins/spatial_editor_plugin.cpp msgid "Converts rooms for portal culling." -msgstr "" +msgstr "Convertir rooms para eliminar portales." #: editor/plugins/spatial_editor_plugin.cpp msgid "Bottom View" @@ -8253,9 +8219,8 @@ msgid "View Grid" msgstr "Ver CuadrÃcula" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "View Portal Culling" -msgstr "Configuración de ventanilla" +msgstr "Ver Eliminación de Portales" #: editor/plugins/spatial_editor_plugin.cpp #: modules/gridmap/grid_map_editor_plugin.cpp @@ -8576,221 +8541,196 @@ msgid "TextureRegion" msgstr "Región de Textura" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Colors" -msgstr "Color" +msgstr "Colores" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Fonts" -msgstr "TipografÃa" +msgstr "Fonts" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Icons" -msgstr "Icono" +msgstr "Icons" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Styleboxes" -msgstr "Caja de estilos" +msgstr "Styleboxes" #: editor/plugins/theme_editor_plugin.cpp msgid "{num} color(s)" -msgstr "" +msgstr "{num} color(es)" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No colors found." -msgstr "No se encontró ningún sub-recurso." +msgstr "No se encontraron colores." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "{num} constant(s)" -msgstr "Constantes" +msgstr "{num} constant(s)" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No constants found." -msgstr "Constante de color." +msgstr "No se encontraron constants." #: editor/plugins/theme_editor_plugin.cpp msgid "{num} font(s)" -msgstr "" +msgstr "{num} font(s)" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No fonts found." -msgstr "¡No se ha encontrado!" +msgstr "No se han encontrado fonts." #: editor/plugins/theme_editor_plugin.cpp msgid "{num} icon(s)" -msgstr "" +msgstr "{num} icon(s)" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No icons found." -msgstr "¡No se ha encontrado!" +msgstr "No se han encontrado icons." #: editor/plugins/theme_editor_plugin.cpp msgid "{num} stylebox(es)" -msgstr "" +msgstr "{num} stylebox(es)" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No styleboxes found." -msgstr "No se encontró ningún sub-recurso." +msgstr "No se encontraron styleboxes." #: editor/plugins/theme_editor_plugin.cpp msgid "{num} currently selected" -msgstr "" +msgstr "{num} seleccionado actualmente" #: editor/plugins/theme_editor_plugin.cpp msgid "Nothing was selected for the import." -msgstr "" +msgstr "No se ha seleccionado nada para la importación." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Importing Theme Items" -msgstr "Importar Tema" +msgstr "Importar Elementos del Theme" #: editor/plugins/theme_editor_plugin.cpp msgid "Importing items {n}/{n}" -msgstr "" +msgstr "Importación de elementos {n}/{n}" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Updating the editor" -msgstr "¿Salir del editor?" +msgstr "Actualización del editor" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Finalizing" -msgstr "Analizando" +msgstr "Finalizando" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Filter:" -msgstr "Filtro: " +msgstr "Filtro:" #: editor/plugins/theme_editor_plugin.cpp msgid "With Data" -msgstr "" +msgstr "Con Datos" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select by data type:" -msgstr "Selecciona un Nodo" +msgstr "Seleccionar por tipo de datos:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible color items." -msgstr "Selecciona una división para borrarla." +msgstr "Seleccionar todos los elementos color visibles." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible color items and their data." -msgstr "" +msgstr "Seleccionar todos los elementos color visibles y sus datos." #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all visible color items." -msgstr "" +msgstr "Deseleccionar todos los elementos color visibles." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible constant items." -msgstr "¡Selecciona un Ãtem primero!" +msgstr "Seleccionar todos elementos constant visibles." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible constant items and their data." -msgstr "" +msgstr "Seleccionar todos los elementos constant visibles y sus datos." #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all visible constant items." -msgstr "" +msgstr "Deseleccionar todos los elementos constant visibles." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible font items." -msgstr "¡Selecciona un Ãtem primero!" +msgstr "Seleccionar todos los elementos font visibles." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible font items and their data." -msgstr "" +msgstr "Seleccionar todos los elementos font visibles y sus datos." #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all visible font items." -msgstr "" +msgstr "Deseleccionar todos los elementos font visibles." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible icon items." -msgstr "¡Selecciona un Ãtem primero!" +msgstr "Seleccionar todos los elementos icon visibles." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible icon items and their data." -msgstr "¡Selecciona un Ãtem primero!" +msgstr "Seleccionar todos los elementos icon visibles y sus datos." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Deselect all visible icon items." -msgstr "¡Selecciona un Ãtem primero!" +msgstr "Deseleccionar todos los elementos icon visibles." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible stylebox items." -msgstr "" +msgstr "Seleccionar todos los elementos stylebox visibles." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible stylebox items and their data." -msgstr "" +msgstr "Seleccionar todos los elementos stylebox visibles y sus datos." #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all visible stylebox items." -msgstr "" +msgstr "Deseleccionar todos los elementos stylebox visibles." #: editor/plugins/theme_editor_plugin.cpp msgid "" "Caution: Adding icon data may considerably increase the size of your Theme " "resource." msgstr "" +"Precaución: Añadir datos de iconos puede aumentar considerablemente el " +"tamaño de su recurso Theme." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Collapse types." -msgstr "Colapsar Todo" +msgstr "Tipos de colapso." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Expand types." -msgstr "Expandir Todo" +msgstr "Expandir tipos." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all Theme items." -msgstr "Selecciona un Archivo de Plantilla" +msgstr "Seleccionar todos los elementos del Theme." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select With Data" -msgstr "Seleccionar Puntos" +msgstr "Seleccionar Con Datos" #: editor/plugins/theme_editor_plugin.cpp msgid "Select all Theme items with item data." -msgstr "" +msgstr "Seleccionar todos los elementos del Theme con los datos del elemento." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Deselect All" -msgstr "Seleccionar Todo" +msgstr "Deseleccionar Todo" #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all Theme items." -msgstr "" +msgstr "Deseleccionar todos los elementos del Theme." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Import Selected" -msgstr "Importar Escena" +msgstr "Importar Seleccionado" #: editor/plugins/theme_editor_plugin.cpp msgid "" @@ -8798,271 +8738,248 @@ msgid "" "closing this window.\n" "Close anyway?" msgstr "" +"En la pestaña de Elementos de Importación se han seleccionado algunos " +"elementos. La selección se perderá al cerrar esta ventana.\n" +"¿Cerrar de todos modos?" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All Color Items" -msgstr "Eliminar Todos los Ãtems" +msgstr "Eliminar Todos los Elementos de Color" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Item" -msgstr "Eliminar Ãtem" +msgstr "Cambiar Nombre de Elemento" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All Constant Items" -msgstr "Eliminar Todos los Ãtems" +msgstr "Eliminar Todos los Elementos Constant" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All Font Items" -msgstr "Eliminar Todos los Ãtems" +msgstr "Eliminar Todos los Elementos Font" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All Icon Items" -msgstr "Eliminar Todos los Ãtems" +msgstr "Eliminar Todos los Elementos de Iconos" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All StyleBox Items" -msgstr "Eliminar Todos los Ãtems" +msgstr "Eliminar Todos los Elementos de StyleBox" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Color Item" -msgstr "Añadir Clases de Ãtems" +msgstr "Añadir Elemento Color" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Constant Item" -msgstr "Añadir Clases de Ãtems" +msgstr "Añadir Elemento Constant" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Font Item" -msgstr "Añadir Ãtem" +msgstr "Añadir Elemento Font" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Icon Item" -msgstr "Añadir Ãtem" +msgstr "Añadir Elemento Icono" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Stylebox Item" -msgstr "Añadir Todos los Ãtems" +msgstr "Añadir Elemento Stylebox" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Color Item" -msgstr "Eliminar Clases de Ãtems" +msgstr "Cambiar Nombre del Elemento Color" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Constant Item" -msgstr "Eliminar Clases de Ãtems" +msgstr "Cambiar Nombre del Elemento Constant" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Font Item" -msgstr "Renombrar Nodo" +msgstr "Renombrar Elemento Font" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Icon Item" -msgstr "Renombrar Nodo" +msgstr "Renombrar Elemento Icon" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Stylebox Item" -msgstr "Eliminar Ãtem Seleccionado" +msgstr "Renombrar Elemento Stylebox" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Invalid file, not a Theme resource." -msgstr "Archivo inválido. No es un layout de bus de audio." +msgstr "Archivo inválido, no es un recurso del Theme." #: editor/plugins/theme_editor_plugin.cpp msgid "Invalid file, same as the edited Theme resource." -msgstr "" +msgstr "Archivo inválido, igual que el recurso del Theme editado." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Manage Theme Items" -msgstr "Administrar Plantillas" +msgstr "Administrar Elementos del Theme" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Edit Items" -msgstr "Ãtem Editable" +msgstr "Editar Elementos" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Types:" -msgstr "Tipo:" +msgstr "Tipos:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Type:" -msgstr "Tipo:" +msgstr "Añadir Tipo:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Item:" -msgstr "Añadir Ãtem" +msgstr "Añadir Elemento:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add StyleBox Item" -msgstr "Añadir Todos los Ãtems" +msgstr "Añadir Elemento StyleBox" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove Items:" -msgstr "Eliminar Ãtem" +msgstr "Eliminar Elemento:" #: editor/plugins/theme_editor_plugin.cpp msgid "Remove Class Items" -msgstr "Eliminar Clases de Ãtems" +msgstr "Eliminar Clases de Elementos" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove Custom Items" -msgstr "Eliminar Clases de Ãtems" +msgstr "Eliminar Elementos Personalizados" #: editor/plugins/theme_editor_plugin.cpp msgid "Remove All Items" -msgstr "Eliminar Todos los Ãtems" +msgstr "Eliminar Todos los Elementos" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Theme Item" -msgstr "Elementos del tema de interfaz" +msgstr "Añadir Elemento del Theme" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Old Name:" -msgstr "Nombre del Nodo:" +msgstr "Nombre Antiguo:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Import Items" -msgstr "Importar Tema" +msgstr "Importar Elementos" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Default Theme" -msgstr "Predeterminado" +msgstr "Theme Predeterminado" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Editor Theme" -msgstr "Editar Tema" +msgstr "Editor de Themes" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select Another Theme Resource:" -msgstr "Eliminar Recurso" +msgstr "Seleccionar Otro Recurso del Theme:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Another Theme" -msgstr "Importar Tema" +msgstr "Otro Theme" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Confirm Item Rename" -msgstr "Renombrar pista de animación" +msgstr "Confirmar Cambio de Nombre del Elemento" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Cancel Item Rename" -msgstr "Renombrar por lote" +msgstr "Cancelar Cambio de Nombre del Elemento" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Override Item" -msgstr "Anulaciones" +msgstr "Elemento de Anulación" #: editor/plugins/theme_editor_plugin.cpp msgid "Unpin this StyleBox as a main style." -msgstr "" +msgstr "Quitar este StyleBox como estilo principal." #: editor/plugins/theme_editor_plugin.cpp msgid "" "Pin this StyleBox as a main style. Editing its properties will update the " "same properties in all other StyleBoxes of this type." msgstr "" +"Establecer este StyleBox como un estilo principal. La edición de sus " +"propiedades actualizará las mismas propiedades en todos los demás StyleBoxes " +"de este tipo." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Type" -msgstr "Tipo" +msgstr "Añadir Tipo" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Item Type" -msgstr "Añadir Ãtem" +msgstr "Añadir Tipo de Elemento" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Node Types:" -msgstr "Tipo de nodo" +msgstr "Tipo de nodo:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Show Default" -msgstr "Cargar Valores por Defecto" +msgstr "Mostrar Por Defecto" #: editor/plugins/theme_editor_plugin.cpp msgid "Show default type items alongside items that have been overridden." msgstr "" +"Mostrar los elementos de tipo por defecto junto a los elementos que han sido " +"anulados." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Override All" -msgstr "Anulaciones" +msgstr "Anular Todo" #: editor/plugins/theme_editor_plugin.cpp msgid "Override all default type items." -msgstr "" +msgstr "Anular todos los elementos de tipo por defecto." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Theme:" -msgstr "Tema" +msgstr "Theme:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Manage Items..." -msgstr "Administrar Plantillas de Exportación..." +msgstr "Administrar Elementos..." #: editor/plugins/theme_editor_plugin.cpp msgid "Add, remove, organize and import Theme items." -msgstr "" +msgstr "Añadir, eliminar, organizar e importar elementos del Theme." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Preview" -msgstr "Vista Previa" +msgstr "Añadir Vista Previa" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Default Preview" -msgstr "Actualizar Vista Previa" +msgstr "Vista Previa Por Defecto" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select UI Scene:" -msgstr "Selecciona una Malla de Origen:" +msgstr "Seleccionar Escena UI:" #: editor/plugins/theme_editor_preview.cpp msgid "" "Toggle the control picker, allowing to visually select control types for " "edit." msgstr "" +"Activar el selector de controles, lo que permite seleccionar visualmente los " +"tipos de control para su edición." #: editor/plugins/theme_editor_preview.cpp msgid "Toggle Button" @@ -9074,32 +8991,31 @@ msgstr "Botón Desactivado" #: editor/plugins/theme_editor_preview.cpp msgid "Item" -msgstr "Ãtem" +msgstr "Elemento" #: editor/plugins/theme_editor_preview.cpp msgid "Disabled Item" -msgstr "Desactivar Ãtem" +msgstr "Desactivar Elemento" #: editor/plugins/theme_editor_preview.cpp msgid "Check Item" -msgstr "Activar Ãtem" +msgstr "Activar Elemento" #: editor/plugins/theme_editor_preview.cpp msgid "Checked Item" -msgstr "Ãtem Activado" +msgstr "Elemento Activado" #: editor/plugins/theme_editor_preview.cpp msgid "Radio Item" -msgstr "Radio Ãtem" +msgstr "Radio Elemento" #: editor/plugins/theme_editor_preview.cpp msgid "Checked Radio Item" -msgstr "Radio Ãtem Activo" +msgstr "Radio Elemento Activo" #: editor/plugins/theme_editor_preview.cpp -#, fuzzy msgid "Named Separator" -msgstr "Separador con nombre." +msgstr "Separador con nombre" #: editor/plugins/theme_editor_preview.cpp msgid "Submenu" @@ -9107,11 +9023,11 @@ msgstr "Submenú" #: editor/plugins/theme_editor_preview.cpp msgid "Subitem 1" -msgstr "SubÃtem 1" +msgstr "Subelemento 1" #: editor/plugins/theme_editor_preview.cpp msgid "Subitem 2" -msgstr "SubÃtem 2" +msgstr "Subelemento 2" #: editor/plugins/theme_editor_preview.cpp msgid "Has" @@ -9139,7 +9055,7 @@ msgstr "Tab 3" #: editor/plugins/theme_editor_preview.cpp msgid "Editable Item" -msgstr "Ãtem Editable" +msgstr "Elemento Editable" #: editor/plugins/theme_editor_preview.cpp msgid "Subtree" @@ -9152,19 +9068,19 @@ msgstr "Tienes, muchas, opciones" #: editor/plugins/theme_editor_preview.cpp msgid "Invalid path, the PackedScene resource was probably moved or removed." msgstr "" +"Ruta inválida, el recurso PackedScene probablemente fue movido o eliminado." #: editor/plugins/theme_editor_preview.cpp msgid "Invalid PackedScene resource, must have a Control node at its root." -msgstr "" +msgstr "Recurso PackedScene inválido, debe tener un nodo Control en raÃz." #: editor/plugins/theme_editor_preview.cpp -#, fuzzy msgid "Invalid file, not a PackedScene resource." -msgstr "Archivo inválido. No es un layout de bus de audio." +msgstr "Archivo inválido, no es un recurso PackedScene." #: editor/plugins/theme_editor_preview.cpp msgid "Reload the scene to reflect its most actual state." -msgstr "" +msgstr "Recargar la escena para reflejar su estado actual." #: editor/plugins/tile_map_editor_plugin.cpp msgid "Erase Selection" @@ -10569,9 +10485,8 @@ msgid "VisualShader" msgstr "VisualShader" #: editor/plugins/visual_shader_editor_plugin.cpp -#, fuzzy msgid "Edit Visual Property:" -msgstr "Editar Propiedad Visual" +msgstr "Editar Propiedad Visual:" #: editor/plugins/visual_shader_editor_plugin.cpp msgid "Visual Shader Mode Changed" @@ -10698,9 +10613,8 @@ msgid "Script" msgstr "Script" #: editor/project_export.cpp -#, fuzzy msgid "GDScript Export Mode:" -msgstr "Modo de exportación de scripts:" +msgstr "Modo de Exportación GDScript:" #: editor/project_export.cpp msgid "Text" @@ -10708,21 +10622,20 @@ msgstr "Texto" #: editor/project_export.cpp msgid "Compiled Bytecode (Faster Loading)" -msgstr "" +msgstr "Bytecode Compilado (Carga Más Rápida)" #: editor/project_export.cpp msgid "Encrypted (Provide Key Below)" msgstr "Encriptado (Proveer la Clave Debajo)" #: editor/project_export.cpp -#, fuzzy msgid "Invalid Encryption Key (must be 64 hexadecimal characters long)" -msgstr "Llave de Encriptación Inválida (debe tener 64 caracteres de largo)" +msgstr "" +"Llave de Encriptación Inválida (debe tener 64 caracteres hexadecimales)" #: editor/project_export.cpp -#, fuzzy msgid "GDScript Encryption Key (256-bits as hexadecimal):" -msgstr "Clave de cifrado de scripts (256-bits en hexadecimal):" +msgstr "Llave de Encriptación GDScript (256-bits en hexadecimal):" #: editor/project_export.cpp msgid "Export PCK/Zip" @@ -10796,7 +10709,6 @@ msgid "Imported Project" msgstr "Proyecto Importado" #: editor/project_manager.cpp -#, fuzzy msgid "Invalid project name." msgstr "Nombre de Proyecto Inválido." @@ -11015,7 +10927,7 @@ msgid "" "Can't run project: Assets need to be imported.\n" "Please edit the project to trigger the initial import." msgstr "" -"No se puede ejecutar el proyecto: los assets necesitan ser importados.\n" +"No se puede ejecutar el proyecto: Los assets necesitan ser importados.\n" "Por favor, edita el proyecto para activar el importado inicial." #: editor/project_manager.cpp @@ -11023,14 +10935,12 @@ msgid "Are you sure to run %d projects at once?" msgstr "¿Estás seguro de que vas a ejecutar %d proyectos a la vez?" #: editor/project_manager.cpp -#, fuzzy msgid "Remove %d projects from the list?" -msgstr "Seleccionar dispositivo de la lista" +msgstr "¿Quitar %d proyectos de la lista?" #: editor/project_manager.cpp -#, fuzzy msgid "Remove this project from the list?" -msgstr "Seleccionar dispositivo de la lista" +msgstr "¿Quitar este proyecto de la lista?" #: editor/project_manager.cpp msgid "" @@ -11064,9 +10974,8 @@ msgid "Project Manager" msgstr "Administrador de Proyectos" #: editor/project_manager.cpp -#, fuzzy msgid "Local Projects" -msgstr "Proyectos" +msgstr "Proyectos Locales" #: editor/project_manager.cpp msgid "Loading, please wait..." @@ -11077,23 +10986,20 @@ msgid "Last Modified" msgstr "Ultima Modificación" #: editor/project_manager.cpp -#, fuzzy msgid "Edit Project" -msgstr "Exportar Proyecto" +msgstr "Editar Proyecto" #: editor/project_manager.cpp -#, fuzzy msgid "Run Project" -msgstr "Renombrar Proyecto" +msgstr "Reproducir Proyecto" #: editor/project_manager.cpp msgid "Scan" msgstr "Escanear" #: editor/project_manager.cpp -#, fuzzy msgid "Scan Projects" -msgstr "Proyectos" +msgstr "Escanear Proyectos" #: editor/project_manager.cpp msgid "Select a Folder to Scan" @@ -11104,14 +11010,12 @@ msgid "New Project" msgstr "Nuevo Proyecto" #: editor/project_manager.cpp -#, fuzzy msgid "Import Project" -msgstr "Proyecto Importado" +msgstr "Importar Proyecto" #: editor/project_manager.cpp -#, fuzzy msgid "Remove Project" -msgstr "Renombrar Proyecto" +msgstr "Eliminar Proyecto" #: editor/project_manager.cpp msgid "Remove Missing" @@ -11122,9 +11026,8 @@ msgid "About" msgstr "Acerca de" #: editor/project_manager.cpp -#, fuzzy msgid "Asset Library Projects" -msgstr "Biblioteca de Assets" +msgstr "Proyectos de la LibrerÃa de Assets" #: editor/project_manager.cpp msgid "Restart Now" @@ -11136,7 +11039,7 @@ msgstr "Eliminar Todos" #: editor/project_manager.cpp msgid "Also delete project contents (no undo!)" -msgstr "" +msgstr "También eliminar el contenido del proyecto (¡no se puede deshacer!)" #: editor/project_manager.cpp msgid "Can't run project" @@ -11148,24 +11051,22 @@ msgid "" "Would you like to explore official example projects in the Asset Library?" msgstr "" "Actualmente no tienes ningún proyecto.\n" -"¿Quieres explorar proyectos de ejemplo oficiales en la Biblioteca de Assets?" +"¿Quieres explorar proyectos de ejemplo oficiales en la LibrerÃa de Assets?" #: editor/project_manager.cpp -#, fuzzy msgid "Filter projects" -msgstr "Filtrar propiedades" +msgstr "Filtrar proyectos" #: editor/project_manager.cpp -#, fuzzy msgid "" "This field filters projects by name and last path component.\n" "To filter projects by name and full path, the query must contain at least " "one `/` character." msgstr "" -"La casilla de búsqueda filtra los proyectos por nombre y el último " -"componente de la ruta.\n" -"Para filtrar los proyectos por nombre y ruta completa, la consulta debe " -"contener al menos un carácter `/`." +"Este campo filtra los proyectos por nombre y por el último componente de la " +"ruta.\n" +"Para filtrar proyectos por nombre y ruta completa, la consulta debe contener " +"al menos un carácter `/`." #: editor/project_settings_editor.cpp msgid "Key " @@ -11173,7 +11074,7 @@ msgstr "Tecla " #: editor/project_settings_editor.cpp msgid "Physical Key" -msgstr "" +msgstr "Tecla FÃsica" #: editor/project_settings_editor.cpp msgid "Joy Button" @@ -11221,7 +11122,7 @@ msgstr "Dispositivo" #: editor/project_settings_editor.cpp msgid " (Physical)" -msgstr "" +msgstr " (FÃsica)" #: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp msgid "Press a Key..." @@ -11321,7 +11222,7 @@ msgstr "Añadir Propiedad Global" #: editor/project_settings_editor.cpp msgid "Select a setting item first!" -msgstr "¡Selecciona un Ãtem primero!" +msgstr "¡Selecciona un elemento primero!" #: editor/project_settings_editor.cpp msgid "No property '%s' exists." @@ -11333,7 +11234,7 @@ msgstr "El ajuste '%s' es interno y no puede ser eliminado." #: editor/project_settings_editor.cpp msgid "Delete Item" -msgstr "Eliminar Ãtem" +msgstr "Eliminar Elemento" #: editor/project_settings_editor.cpp msgid "" @@ -11364,23 +11265,20 @@ msgid "Override for Feature" msgstr "Anulación de la CaracterÃstica" #: editor/project_settings_editor.cpp -#, fuzzy msgid "Add %d Translations" -msgstr "Añadir Traducción" +msgstr "Añadir %d Traducciones" #: editor/project_settings_editor.cpp msgid "Remove Translation" msgstr "Eliminar Traducción" #: editor/project_settings_editor.cpp -#, fuzzy msgid "Translation Resource Remap: Add %d Path(s)" -msgstr "Añadir Remapeo de Recursos" +msgstr "Remapeo de Recursos de Traducción: Añadir %d Ruta(s)" #: editor/project_settings_editor.cpp -#, fuzzy msgid "Translation Resource Remap: Add %d Remap(s)" -msgstr "Añadir Remapeo de Recursos" +msgstr "Remapeo de Recursos de Traducción: Añadir %d Remapeo(s)" #: editor/project_settings_editor.cpp msgid "Change Resource Remap Language" @@ -11825,12 +11723,16 @@ msgstr "¿Eliminar nodo \"%s\"?" msgid "" "Saving the branch as a scene requires having a scene open in the editor." msgstr "" +"Guardar la rama como una escena requiere tener una escena abierta en el " +"editor." #: editor/scene_tree_dock.cpp msgid "" "Saving the branch as a scene requires selecting only one node, but you have " "selected %d nodes." msgstr "" +"Guardar la rama como una escena requiere seleccionar sólo un nodo, pero has " +"seleccionado %d nodos." #: editor/scene_tree_dock.cpp msgid "" @@ -11839,6 +11741,11 @@ msgid "" "FileSystem dock context menu\n" "or create an inherited scene using Scene > New Inherited Scene... instead." msgstr "" +"No se puede guardar la rama del nodo raÃz como una escena instanciada.\n" +"Para crear una copia editable de la escena actual, duplÃcala usando el menú " +"contextual del dock Sistema de Archivos\n" +"o crea una escena heredada usando Escena > Nueva Escena Heredada... en su " +"lugar." #: editor/scene_tree_dock.cpp msgid "" @@ -11846,6 +11753,10 @@ msgid "" "To create a variation of a scene, you can make an inherited scene based on " "the instanced scene using Scene > New Inherited Scene... instead." msgstr "" +"No se puede guardar la rama de una escena ya instanciada.\n" +"Para crear una variación de una escena, puedes hacer una escena heredada " +"basada en la escena instanciada usando Escena > Nueva Escena Heredada... en " +"su lugar." #: editor/scene_tree_dock.cpp msgid "Save New Scene As..." @@ -12254,6 +12165,8 @@ msgid "" "Warning: Having the script name be the same as a built-in type is usually " "not desired." msgstr "" +"Advertencia: No es recomendable que el nombre del script sea el mismo que el " +"de un tipo integrado." #: editor/script_create_dialog.cpp msgid "Class Name:" @@ -12325,7 +12238,7 @@ msgstr "Copiar Error" #: editor/script_editor_debugger.cpp msgid "Open C++ Source on GitHub" -msgstr "" +msgstr "Código Abierto C++ en GitHub" #: editor/script_editor_debugger.cpp msgid "Video RAM" @@ -12369,7 +12282,7 @@ msgstr "Monitores" #: editor/script_editor_debugger.cpp msgid "Pick one or more items from the list to display the graph." -msgstr "Elige uno o más Ãtems de la lista para mostrar el gráfico." +msgstr "Elige uno o más elementos de la lista para mostrar el gráfico." #: editor/script_editor_debugger.cpp msgid "List of Video Memory Usage by Resource:" @@ -12503,6 +12416,16 @@ msgstr "Cambiar Altura de la Forma del Cilindro" msgid "Change Ray Shape Length" msgstr "Cambiar Longitud de la Forma del Rayo" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "Establecer Posición de Punto de Curva" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "Establecer Posición de Punto de Curva" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "Cambiar Radio de Cylinder" @@ -12616,14 +12539,12 @@ msgid "Object can't provide a length." msgstr "El objeto no puede proporcionar una longitud." #: modules/gltf/editor_scene_exporter_gltf_plugin.cpp -#, fuzzy msgid "Export Mesh GLTF2" -msgstr "Exportar LibrerÃa de Mallas" +msgstr "Exportar Malla GLTF2" #: modules/gltf/editor_scene_exporter_gltf_plugin.cpp -#, fuzzy msgid "Export GLTF..." -msgstr "Exportar…" +msgstr "Exportar GLTF..." #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Next Plane" @@ -12666,9 +12587,8 @@ msgid "GridMap Paint" msgstr "Pintar GridMap" #: modules/gridmap/grid_map_editor_plugin.cpp -#, fuzzy msgid "GridMap Selection" -msgstr "Rellenar Selección en GridMap" +msgstr "Seleccionar GridMap" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Grid Map" @@ -12922,14 +12842,12 @@ msgid "Add Output Port" msgstr "Añadir Puerto de Salida" #: modules/visual_script/visual_script_editor.cpp -#, fuzzy msgid "Change Port Type" -msgstr "Cambiar Tipo" +msgstr "Cambiar Tipo de Puerto" #: modules/visual_script/visual_script_editor.cpp -#, fuzzy msgid "Change Port Name" -msgstr "Cambiar nombre del puerto de entrada" +msgstr "Cambiar Nombre de Puerto" #: modules/visual_script/visual_script_editor.cpp msgid "Override an existing built-in function." @@ -13044,9 +12962,8 @@ msgid "Add Preload Node" msgstr "Añadir Nodo Preload" #: modules/visual_script/visual_script_editor.cpp -#, fuzzy msgid "Add Node(s)" -msgstr "Añadir Nodo" +msgstr "Añadir Nodo(s)" #: modules/visual_script/visual_script_editor.cpp msgid "Add Node(s) From Tree" @@ -13313,37 +13230,31 @@ msgstr "Seleccionar dispositivo de la lista" #: platform/android/export/export.cpp msgid "Running on %s" -msgstr "" +msgstr "Ejecutar en %s" #: platform/android/export/export.cpp -#, fuzzy msgid "Exporting APK..." -msgstr "Exportar Todo" +msgstr "Exportar APK..." #: platform/android/export/export.cpp -#, fuzzy msgid "Uninstalling..." -msgstr "Desinstalar" +msgstr "Desinstalando..." #: platform/android/export/export.cpp -#, fuzzy msgid "Installing to device, please wait..." -msgstr "Cargando, espera por favor..." +msgstr "Instalando en el dispositivo, espera por favor..." #: platform/android/export/export.cpp -#, fuzzy msgid "Could not install to device: %s" -msgstr "¡No se pudo instanciar la escena!" +msgstr "No se pudo instalar en el dispositivo: %s" #: platform/android/export/export.cpp -#, fuzzy msgid "Running on device..." -msgstr "Ejecutando Script Personalizado..." +msgstr "Ejecutando en el dispositivo..." #: platform/android/export/export.cpp -#, fuzzy msgid "Could not execute on device." -msgstr "No se pudo crear la carpeta." +msgstr "No se ha podido ejecutar en el dispositivo." #: platform/android/export/export.cpp msgid "Unable to find the 'apksigner' tool." @@ -13473,45 +13384,43 @@ msgid "" "directory.\n" "The resulting %s is unsigned." msgstr "" +"No se ha encontrado 'apksigner'.\n" +"Por favor, compruebe que el comando está disponible en el directorio Android " +"SDK build-tools.\n" +"El resultado %s es sin firma." #: platform/android/export/export.cpp msgid "Signing debug %s..." -msgstr "" +msgstr "Firma de depuración %s..." #: platform/android/export/export.cpp -#, fuzzy msgid "Signing release %s..." -msgstr "" -"Escaneando archivos,\n" -"Por favor, espere..." +msgstr "Firmando liberación %s..." #: platform/android/export/export.cpp -#, fuzzy msgid "Could not find keystore, unable to export." -msgstr "No se pudo abrir la plantilla para exportar:" +msgstr "No se pudo encontrar la keystore, no se puedo exportar." #: platform/android/export/export.cpp msgid "'apksigner' returned with error #%d" -msgstr "" +msgstr "'apksigner' ha retornado con error #%d" #: platform/android/export/export.cpp -#, fuzzy msgid "Verifying %s..." -msgstr "Añadiendo %s..." +msgstr "Verificando %s..." #: platform/android/export/export.cpp msgid "'apksigner' verification of %s failed." -msgstr "" +msgstr "La verificación de 'apksigner' de %s ha fallado." #: platform/android/export/export.cpp -#, fuzzy msgid "Exporting for Android" -msgstr "Exportar Todo" +msgstr "Exportando para Android" #: platform/android/export/export.cpp msgid "Invalid filename! Android App Bundle requires the *.aab extension." msgstr "" -"¡Nombre de archivo inválido! Android App Bundle requiere la extensión *.aab." +"¡Nombre del archivo inválido! Android App Bundle requiere la extensión *.aab." #: platform/android/export/export.cpp msgid "APK Expansion not compatible with Android App Bundle." @@ -13523,7 +13432,7 @@ msgstr "¡Nombre de archivo inválido! Android APK requiere la extensión *.apk. #: platform/android/export/export.cpp msgid "Unsupported export format!\n" -msgstr "" +msgstr "¡Formato de exportación no compatible!\n" #: platform/android/export/export.cpp msgid "" @@ -13551,16 +13460,16 @@ msgstr "" msgid "" "Unable to overwrite res://android/build/res/*.xml files with project name" msgstr "" +"No se puede sobrescribir los archivos res://android/build/res/*.xml con el " +"nombre del proyecto" #: platform/android/export/export.cpp -#, fuzzy msgid "Could not export project files to gradle project\n" -msgstr "No se encontró project.godot en la ruta del proyecto." +msgstr "No se pueden exportar los archivos del proyecto a un proyecto gradle\n" #: platform/android/export/export.cpp -#, fuzzy msgid "Could not write expansion package file!" -msgstr "No se puede escribir en el archivo:" +msgstr "¡No se pudo escribir el archivo del paquete de expansión!" #: platform/android/export/export.cpp msgid "Building Android Project (gradle)" @@ -13588,21 +13497,20 @@ msgstr "" "directorio del proyecto de gradle para ver los resultados." #: platform/android/export/export.cpp -#, fuzzy msgid "Package not found: %s" -msgstr "No se encontró la animación: '%s'" +msgstr "Paquete no encontrado:% s" #: platform/android/export/export.cpp -#, fuzzy msgid "Creating APK..." -msgstr "Creando contornos..." +msgstr "Creando APK..." #: platform/android/export/export.cpp -#, fuzzy msgid "" "Could not find template APK to export:\n" "%s" -msgstr "No se pudo abrir la plantilla para exportar:" +msgstr "" +"No se pudo encontrar la plantilla APK para exportar:\n" +"%s" #: platform/android/export/export.cpp msgid "" @@ -13611,16 +13519,18 @@ msgid "" "Please build a template with all required libraries, or uncheck the missing " "architectures in the export preset." msgstr "" +"Faltan bibliotecas en la plantilla de exportación para las arquitecturas " +"seleccionadas: %s.\n" +"Por favor, construya una plantilla con todas las bibliotecas necesarias, o " +"desmarque las arquitecturas que faltan en el preajuste de exportación." #: platform/android/export/export.cpp -#, fuzzy msgid "Adding files..." -msgstr "Añadiendo %s..." +msgstr "Añadiendo archivos ..." #: platform/android/export/export.cpp -#, fuzzy msgid "Could not export project files" -msgstr "No se puede escribir en el archivo:" +msgstr "No se pudieron exportar los archivos del proyecto" #: platform/android/export/export.cpp msgid "Aligning APK..." @@ -13628,15 +13538,15 @@ msgstr "Alineando APK..." #: platform/android/export/export.cpp msgid "Could not unzip temporary unaligned APK." -msgstr "" +msgstr "No se pudo descomprimir el APK no alineado temporal." #: platform/iphone/export/export.cpp platform/osx/export/export.cpp msgid "Identifier is missing." -msgstr "Identificador no encontrado." +msgstr "Falta el identificador." #: platform/iphone/export/export.cpp platform/osx/export/export.cpp msgid "The character '%s' is not allowed in Identifier." -msgstr "El carácter '%s' no esta permitido como identificador." +msgstr "El carácter '% s' no está permitido en el Identificador." #: platform/iphone/export/export.cpp msgid "App Store Team ID not specified - cannot configure the project." @@ -13676,45 +13586,40 @@ msgid "Could not write file:" msgstr "No se puede escribir en el archivo:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Could not read file:" -msgstr "No se puede escribir en el archivo:" +msgstr "No se pudo leer el archivo:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Could not read HTML shell:" -msgstr "No se puede leer shell HTML personalizada:" +msgstr "No se pudo leer el shell HTML:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Could not create HTTP server directory:" -msgstr "No se pudo crear la carpeta." +msgstr "No se pudo crear el directorio del servidor HTTP:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Error starting HTTP server:" -msgstr "Error al guardar escena." +msgstr "Error al iniciar el servidor HTTP:" #: platform/osx/export/export.cpp -#, fuzzy msgid "Invalid bundle identifier:" -msgstr "Identificador inválido:" +msgstr "Identificador de paquete no válido:" #: platform/osx/export/export.cpp msgid "Notarization: code signing required." -msgstr "" +msgstr "Notarización: se requiere firma de código." #: platform/osx/export/export.cpp msgid "Notarization: hardened runtime required." -msgstr "" +msgstr "Notarización: se requiere tiempo de ejecución reforzado." #: platform/osx/export/export.cpp msgid "Notarization: Apple ID name not specified." -msgstr "" +msgstr "Notarización: nombre de ID de Apple no especificado." #: platform/osx/export/export.cpp msgid "Notarization: Apple ID password not specified." -msgstr "" +msgstr "Notarización: contraseña de ID de Apple no especificada." #: platform/uwp/export/export.cpp msgid "Invalid package short name." @@ -14162,6 +14067,9 @@ msgid "" "longer has any effect.\n" "To remove this warning, disable the GIProbe's Compress property." msgstr "" +"La propiedad Compress de GIProbe ha quedado obsoleta debido a errores " +"conocidos y ya no tiene ningún efecto.\n" +"Para eliminar esta advertencia, desactiva la propiedad Compress de GIProbe." #: scene/3d/light.cpp msgid "A SpotLight with an angle wider than 90 degrees cannot cast shadows." @@ -14253,15 +14161,15 @@ msgstr "El nodo A y el nodo B deben ser diferentes PhysicsBody" #: scene/3d/portal.cpp msgid "The RoomManager should not be a child or grandchild of a Portal." -msgstr "" +msgstr "El RoomManager no debe ser hijo o nieto de un Portal." #: scene/3d/portal.cpp msgid "A Room should not be a child or grandchild of a Portal." -msgstr "" +msgstr "Una Room no debe ser hijo o nieto de un Portal." #: scene/3d/portal.cpp msgid "A RoomGroup should not be a child or grandchild of a Portal." -msgstr "" +msgstr "Un RoomGroup no debe ser hijo o nieto de un Portal." #: scene/3d/remote_transform.cpp msgid "" @@ -14273,42 +14181,83 @@ msgstr "" #: scene/3d/room.cpp msgid "A Room cannot have another Room as a child or grandchild." -msgstr "" +msgstr "Una Room no puede tener otra Room como hija o nieta." #: scene/3d/room.cpp msgid "The RoomManager should not be placed inside a Room." -msgstr "" +msgstr "El RoomManager no debe ubicarse dentro de una Room." #: scene/3d/room.cpp msgid "A RoomGroup should not be placed inside a Room." -msgstr "" +msgstr "Un RoomGroup no debe colocarse dentro de una Room." #: scene/3d/room.cpp msgid "" "Room convex hull contains a large number of planes.\n" "Consider simplifying the room bound in order to increase performance." msgstr "" +"El cuerpo convexo de la room contiene un gran número de planos.\n" +"Considera la posibilidad de simplificar los lÃmites de la room para aumentar " +"el rendimiento." #: scene/3d/room_group.cpp msgid "The RoomManager should not be placed inside a RoomGroup." -msgstr "" +msgstr "El RoomManager no debe colocarse dentro de un RoomGroup." #: scene/3d/room_manager.cpp msgid "The RoomList has not been assigned." -msgstr "" +msgstr "La RoomList no ha sido asignada." #: scene/3d/room_manager.cpp msgid "The RoomList node should be a Spatial (or derived from Spatial)." -msgstr "" +msgstr "El nodo RoomList debe ser un Spatial (o derivado de Spatial)." #: scene/3d/room_manager.cpp msgid "" "Portal Depth Limit is set to Zero.\n" "Only the Room that the Camera is in will render." msgstr "" +"El LÃmite de Profundidad del Portal está ajustado a cero.\n" +"Sólo se renderizará la room en la que se encuentra la cámara." #: scene/3d/room_manager.cpp msgid "There should only be one RoomManager in the SceneTree." +msgstr "Sólo debe haber un RoomManager en el SceneTree." + +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." msgstr "" #: scene/3d/soft_body.cpp @@ -14375,7 +14324,7 @@ msgstr "No se encontró la animación: '%s'" #: scene/animation/animation_player.cpp msgid "Anim Apply Reset" -msgstr "" +msgstr "Aplicar Reset de la Animación" #: scene/animation/animation_tree.cpp msgid "In node '%s', invalid animation: '%s'." @@ -14553,25 +14502,30 @@ msgid "Invalid comparison function for that type." msgstr "Función de comparación inválida para este tipo." #: servers/visual/shader_language.cpp -#, fuzzy msgid "Varying may not be assigned in the '%s' function." -msgstr "Solo se pueden asignar variaciones en funciones de vértice." +msgstr "No se puede asignar la variable en la función '%s'." #: servers/visual/shader_language.cpp msgid "" "Varyings which assigned in 'vertex' function may not be reassigned in " "'fragment' or 'light'." msgstr "" +"Las variaciones asignadas en función 'vértice' no pueden reasignarse en " +"'fragmento' o 'luz'." #: servers/visual/shader_language.cpp msgid "" "Varyings which assigned in 'fragment' function may not be reassigned in " "'vertex' or 'light'." msgstr "" +"Varyings Cuál asignó en 'fragmento' la función no puede ser reasignada en " +"'vértice' o 'ligero'." #: servers/visual/shader_language.cpp msgid "Fragment-stage varying could not been accessed in custom function!" msgstr "" +"¡No se pudo acceder a la variación de la etapa de fragmento en la función " +"personalizada!" #: servers/visual/shader_language.cpp msgid "Assignment to function." @@ -15771,9 +15725,6 @@ msgstr "Las constantes no pueden modificarse." #~ msgid "I see..." #~ msgstr "Ya veo..." -#~ msgid "Can't open '%s'." -#~ msgstr "No se puede abrir '%s'." - #~ msgid "Ugh" #~ msgstr "Vaya" diff --git a/editor/translations/es_AR.po b/editor/translations/es_AR.po index 53041c74fd..d5c955a347 100644 --- a/editor/translations/es_AR.po +++ b/editor/translations/es_AR.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2021-07-29 02:33+0000\n" +"PO-Revision-Date: 2021-08-06 06:47+0000\n" "Last-Translator: Lisandro Lorea <lisandrolorea@gmail.com>\n" "Language-Team: Spanish (Argentina) <https://hosted.weblate.org/projects/" "godot-engine/godot/es_AR/>\n" @@ -30,7 +30,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.7.2-dev\n" +"X-Generator: Weblate 4.8-dev\n" #: core/math/expression.cpp modules/gdscript/gdscript_functions.cpp #: modules/visual_script/visual_script_builtin_funcs.cpp @@ -353,6 +353,7 @@ msgstr "Cambiar Modo de Bucle de Animación" msgid "Remove Anim Track" msgstr "Quitar pista de animación" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Crear NUEVO track para %s e insertar clave?" @@ -377,10 +378,28 @@ msgstr "Crear" msgid "Anim Insert" msgstr "Insertar Anim" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "No se puede abrir '%s'." + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animación" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "Un AnimationPlayer no puede animarse a sà mismo, solo a otros players." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "No existe la propiedad '%s'." + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Crear e Insertar Animación" @@ -591,9 +610,8 @@ msgid "Go to Previous Step" msgstr "Ir a Paso Previo" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Apply Reset" -msgstr "Resetear" +msgstr "Aplicar Reset" #: editor/animation_track_editor.cpp msgid "Optimize Animation" @@ -612,9 +630,8 @@ msgid "Use Bezier Curves" msgstr "Usar Curvas Bezier" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Create RESET Track(s)" -msgstr "Pegar Pistas" +msgstr "Crear RESET Track(s)" #: editor/animation_track_editor.cpp msgid "Anim. Optimizer" @@ -940,9 +957,8 @@ msgid "Edit..." msgstr "Editar..." #: editor/connections_dialog.cpp -#, fuzzy msgid "Go to Method" -msgstr "Ir Al Método" +msgstr "Ir al Método" #: editor/create_dialog.cpp msgid "Change %s Type" @@ -960,9 +976,9 @@ msgstr "Crear Nuevo %s" msgid "No results for \"%s\"." msgstr "No hay resultados para \"%s\"." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." -msgstr "" +msgstr "No hay descripción disponible para %s." #: editor/create_dialog.cpp editor/editor_file_dialog.cpp #: editor/filesystem_dock.cpp @@ -1260,9 +1276,8 @@ msgid "Error opening asset file for \"%s\" (not in ZIP format)." msgstr "Error al abrir el archivo de paquete (no esta en formato ZIP)." #: editor/editor_asset_installer.cpp -#, fuzzy msgid "%s (already exists)" -msgstr "%s (Ya existe)" +msgstr "%s (ya existe)" #: editor/editor_asset_installer.cpp msgid "Contents of asset \"%s\" - %d file(s) conflict with your project:" @@ -1277,19 +1292,16 @@ msgid "Uncompressing Assets" msgstr "Descomprimiendo Assets" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "The following files failed extraction from asset \"%s\":" -msgstr "Los siguientes archivos no se pudieron extraer del paquete:" +msgstr "Falló la extracción de los siguientes archivos del asset \"%s\":" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "(and %s more files)" -msgstr "Y %d archivos más." +msgstr "(y %s archivos más)" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Asset \"%s\" installed successfully!" -msgstr "El Paquete se instaló exitosamente!" +msgstr "El asset \"%s\" se instaló exitosamente!" #: editor/editor_asset_installer.cpp #: editor/plugins/asset_library_editor_plugin.cpp @@ -1301,9 +1313,8 @@ msgid "Install" msgstr "Instalar" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Asset Installer" -msgstr "Instalador de Paquetes" +msgstr "Instalador de Assets" #: editor/editor_audio_buses.cpp msgid "Speakers" @@ -1366,7 +1377,6 @@ msgid "Bypass" msgstr "Bypass" #: editor/editor_audio_buses.cpp -#, fuzzy msgid "Bus Options" msgstr "Opciones de Bus" @@ -1491,7 +1501,7 @@ msgstr "No debe coincidir con el nombre de una clase ya existente del motor." #: editor/editor_autoload_settings.cpp msgid "Must not collide with an existing built-in type name." -msgstr "No debe coincidir con el nombre de un tipo built-in ya existente." +msgstr "No debe coincidir con el nombre de un tipo integrado ya existente." #: editor/editor_autoload_settings.cpp msgid "Must not collide with an existing global constant name." @@ -1534,13 +1544,12 @@ msgid "Can't add autoload:" msgstr "No se puede agregar autoload:" #: editor/editor_autoload_settings.cpp -#, fuzzy msgid "%s is an invalid path. File does not exist." -msgstr "El archivo existe." +msgstr "%s es una ruta inválida. El archivo no existe." #: editor/editor_autoload_settings.cpp msgid "%s is an invalid path. Not in resource path (res://)." -msgstr "" +msgstr "%s es una ruta inválida. No esta en la ruta de recursos (res://)." #: editor/editor_autoload_settings.cpp msgid "Add AutoLoad" @@ -1564,9 +1573,8 @@ msgid "Name" msgstr "Nombre" #: editor/editor_autoload_settings.cpp -#, fuzzy msgid "Global Variable" -msgstr "Variable" +msgstr "Variable Global" #: editor/editor_data.cpp msgid "Paste Params" @@ -1742,29 +1750,32 @@ msgstr "Dock de Importación" #: editor/editor_feature_profile.cpp msgid "Allows to view and edit 3D scenes." -msgstr "" +msgstr "Permite ver y editar escenas 3D." #: editor/editor_feature_profile.cpp msgid "Allows to edit scripts using the integrated script editor." -msgstr "" +msgstr "Permite editar scripts usando el editor de scripts integrado." #: editor/editor_feature_profile.cpp msgid "Provides built-in access to the Asset Library." -msgstr "" +msgstr "Provee acceso integrado a la Biblioteca de Assets." #: editor/editor_feature_profile.cpp msgid "Allows editing the node hierarchy in the Scene dock." -msgstr "" +msgstr "Permite editar la jerarquÃa de nodos en el panel de Escena." #: editor/editor_feature_profile.cpp msgid "" "Allows to work with signals and groups of the node selected in the Scene " "dock." msgstr "" +"Permite trabajar con señales y grupos del nodo seleccionado en el panel de " +"Escena." #: editor/editor_feature_profile.cpp msgid "Allows to browse the local file system via a dedicated dock." msgstr "" +"Permite explorar el sistema de archivos local a través de un panel dedicado." #: editor/editor_feature_profile.cpp msgid "" @@ -1773,13 +1784,12 @@ msgid "" msgstr "" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "(current)" -msgstr "(Actual)" +msgstr "(actual)" #: editor/editor_feature_profile.cpp msgid "(none)" -msgstr "" +msgstr "(ninguno)" #: editor/editor_feature_profile.cpp msgid "Remove currently selected profile, '%s'? Cannot be undone." @@ -1815,19 +1825,16 @@ msgid "Enable Contextual Editor" msgstr "Activar el Editor Contextual" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Class Properties:" -msgstr "Propiedades:" +msgstr "Propiedades de Clase:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Main Features:" -msgstr "CaracterÃsticas" +msgstr "CaracterÃsticas Principales:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Nodes and Classes:" -msgstr "Clases Activadas:" +msgstr "Nodos y Clases:" #: editor/editor_feature_profile.cpp msgid "File '%s' format is invalid, import aborted." @@ -1847,23 +1854,20 @@ msgid "Error saving profile to path: '%s'." msgstr "Error al guardar el perfil en la ruta: '%s'." #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Reset to Default" -msgstr "Restablecer Valores Por Defecto" +msgstr "Restablecer a Valores Por Defecto" #: editor/editor_feature_profile.cpp msgid "Current Profile:" msgstr "Perfil Actual:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Create Profile" -msgstr "Borrar Perfil" +msgstr "Crear Perfil" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Remove Profile" -msgstr "Remover Tile" +msgstr "Remover Perfil" #: editor/editor_feature_profile.cpp msgid "Available Profiles:" @@ -1883,14 +1887,12 @@ msgid "Export" msgstr "Exportar" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Configure Selected Profile:" -msgstr "Perfil Actual:" +msgstr "Configurar Perfil Seleccionado:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Extra Options:" -msgstr "Opciones de Textura" +msgstr "Opciones Extra:" #: editor/editor_feature_profile.cpp msgid "Create or import a profile to edit available classes and properties." @@ -1921,9 +1923,8 @@ msgid "Select Current Folder" msgstr "Seleccionar Carpeta Actual" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp -#, fuzzy msgid "File exists, overwrite?" -msgstr "El Archivo Existe, Sobreescribir?" +msgstr "El archivo existe, sobrescribir?" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp msgid "Select This Folder" @@ -2316,6 +2317,17 @@ msgid "New Window" msgstr "Nueva Ventana" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "Gira cuando la ventana del editor se redibuja." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Los recursos importados no se pueden guardar." @@ -2944,9 +2956,8 @@ msgid "Orphan Resource Explorer..." msgstr "Explorador de Recursos Huérfanos..." #: editor/editor_node.cpp -#, fuzzy msgid "Reload Current Project" -msgstr "Renombrar Proyecto" +msgstr "Volver a Cargar el Proyecto Actual" #: editor/editor_node.cpp msgid "Quit to Project List" @@ -3106,22 +3117,20 @@ msgid "Help" msgstr "Ayuda" #: editor/editor_node.cpp -#, fuzzy msgid "Online Documentation" -msgstr "Abrir Documentación" +msgstr "Documentación En LÃnea" #: editor/editor_node.cpp msgid "Questions & Answers" -msgstr "" +msgstr "Preguntas y Respuestas" #: editor/editor_node.cpp msgid "Report a Bug" msgstr "Reportar un Bug" #: editor/editor_node.cpp -#, fuzzy msgid "Suggest a Feature" -msgstr "Setear un Valor" +msgstr "Sugerir una Feature" #: editor/editor_node.cpp msgid "Send Docs Feedback" @@ -3132,9 +3141,8 @@ msgid "Community" msgstr "Comunidad" #: editor/editor_node.cpp -#, fuzzy msgid "About Godot" -msgstr "Acerca de" +msgstr "Acerca de Godot" #: editor/editor_node.cpp msgid "Support Godot Development" @@ -3186,10 +3194,6 @@ msgid "Save & Restart" msgstr "Guardar y Reiniciar" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "Gira cuando la ventana del editor se redibuja." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "Actualizar Continuamente" @@ -3232,9 +3236,8 @@ msgid "Manage Templates" msgstr "Administrar Plantillas" #: editor/editor_node.cpp -#, fuzzy msgid "Install from file" -msgstr "Instalar Desde Archivo" +msgstr "Instalar desde archivo" #: editor/editor_node.cpp #, fuzzy @@ -3323,9 +3326,8 @@ msgid "Select" msgstr "Seleccionar" #: editor/editor_node.cpp -#, fuzzy msgid "Select Current" -msgstr "Seleccionar Carpeta Actual" +msgstr "Seleccionar Actual" #: editor/editor_node.cpp msgid "Open 2D Editor" @@ -3389,14 +3391,12 @@ msgid "Update" msgstr "Actualizar" #: editor/editor_plugin_settings.cpp -#, fuzzy msgid "Version" -msgstr "Version:" +msgstr "Version" #: editor/editor_plugin_settings.cpp -#, fuzzy msgid "Author" -msgstr "Autores" +msgstr "Autor" #: editor/editor_plugin_settings.cpp #: editor/plugins/version_control_editor_plugin.cpp @@ -3414,7 +3414,6 @@ msgid "Frame Time (ms)" msgstr "Duración de Frame (seg)" #: editor/editor_profiler.cpp -#, fuzzy msgid "Average Time (ms)" msgstr "Tiempo Promedio (seg)" @@ -3564,9 +3563,8 @@ msgid "Paste" msgstr "Pegar" #: editor/editor_resource_picker.cpp editor/property_editor.cpp -#, fuzzy msgid "Convert to %s" -msgstr "Convertir A %s" +msgstr "Convertir a %s" #: editor/editor_resource_picker.cpp editor/property_editor.cpp msgid "New %s" @@ -3640,49 +3638,43 @@ msgstr "Importar Desde Nodo:" #: editor/export_template_manager.cpp msgid "Open the folder containing these templates." -msgstr "" +msgstr "Abrir la carpeta que contiene estas plantillas." #: editor/export_template_manager.cpp msgid "Uninstall these templates." -msgstr "" +msgstr "Desinstalar estas plantillas." #: editor/export_template_manager.cpp -#, fuzzy msgid "There are no mirrors available." -msgstr "No hay ningún archivo '%s'." +msgstr "No hay mirrors disponibles." #: editor/export_template_manager.cpp -#, fuzzy msgid "Retrieving the mirror list..." -msgstr "Recuperando mirrors, esperá, por favor..." +msgstr "Obteniendo la lista de mirrors..." #: editor/export_template_manager.cpp msgid "Starting the download..." -msgstr "" +msgstr "Iniciando la descarga..." #: editor/export_template_manager.cpp msgid "Error requesting URL:" msgstr "Error al solicitar la URL:" #: editor/export_template_manager.cpp -#, fuzzy msgid "Connecting to the mirror..." -msgstr "Conectando al Mirror..." +msgstr "Conectando al mirror..." #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't resolve the requested address." -msgstr "No se ha podido resolver el nombre del host:" +msgstr "No se pudo resolver la dirección indicada." #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't connect to the mirror." -msgstr "No se puede conectar al host:" +msgstr "No se puede conectar al mirror." #: editor/export_template_manager.cpp -#, fuzzy msgid "No response from the mirror." -msgstr "No hay respuesta desde el host:" +msgstr "No hubo respuesta desde el mirror." #: editor/export_template_manager.cpp #: editor/plugins/asset_library_editor_plugin.cpp @@ -3690,18 +3682,16 @@ msgid "Request failed." msgstr "Solicitud fallida." #: editor/export_template_manager.cpp -#, fuzzy msgid "Request ended up in a redirect loop." -msgstr "Solicitud fallida, demasiadas redireccinoes" +msgstr "La solicitud termino en un bucle de redirecciones." #: editor/export_template_manager.cpp -#, fuzzy msgid "Request failed:" -msgstr "Solicitud fallida." +msgstr "Solicitud fallida:" #: editor/export_template_manager.cpp msgid "Download complete; extracting templates..." -msgstr "" +msgstr "Descarga completada; extrayendo plantillas..." #: editor/export_template_manager.cpp msgid "Cannot remove temporary file:" @@ -3728,7 +3718,7 @@ msgstr "" #: editor/export_template_manager.cpp msgid "Best available mirror" -msgstr "" +msgstr "Mejor mirror disponible" #: editor/export_template_manager.cpp msgid "" @@ -3828,15 +3818,17 @@ msgstr "Version Actual:" #: editor/export_template_manager.cpp msgid "Export templates are missing. Download them or install from a file." msgstr "" +"Faltan las plantillas de exportación. Descargalas o instalalas desde un " +"archivo." #: editor/export_template_manager.cpp msgid "Export templates are installed and ready to be used." msgstr "" +"Las plantillas de exportación están instaladas y listas para ser usadas." #: editor/export_template_manager.cpp -#, fuzzy msgid "Open Folder" -msgstr "Abrir Archivo" +msgstr "Abrir Carpeta" #: editor/export_template_manager.cpp msgid "Open the folder containing installed templates for the current version." @@ -3852,13 +3844,22 @@ msgid "Uninstall templates for the current version." msgstr "Valor inicial para el contador" #: editor/export_template_manager.cpp -#, fuzzy msgid "Download from:" -msgstr "Error de Descarga" +msgstr "Descargar desde:" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Ejecutar en el Navegador" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "Copiar Error" #: editor/export_template_manager.cpp msgid "Download and Install" -msgstr "" +msgstr "Descargar e Instalar" #: editor/export_template_manager.cpp msgid "" @@ -3873,14 +3874,12 @@ msgstr "" "versiones de desarrollo." #: editor/export_template_manager.cpp -#, fuzzy msgid "Install from File" -msgstr "Instalar Desde Archivo" +msgstr "Instalar desde Archivo" #: editor/export_template_manager.cpp -#, fuzzy msgid "Install templates from a local file." -msgstr "Importar Plantillas Desde Archivo ZIP" +msgstr "Instalar plantillas desde un archivo local." #: editor/export_template_manager.cpp editor/find_in_files.cpp #: editor/progress_dialog.cpp scene/gui/dialogs.cpp @@ -3888,19 +3887,16 @@ msgid "Cancel" msgstr "Cancelar" #: editor/export_template_manager.cpp -#, fuzzy msgid "Cancel the download of the templates." -msgstr "No se puede abir el zip de plantillas de exportación." +msgstr "Cancelar la descarga de las plantillas." #: editor/export_template_manager.cpp -#, fuzzy msgid "Other Installed Versions:" -msgstr "Versiones Instaladas:" +msgstr "Otras Versiones Instaladas:" #: editor/export_template_manager.cpp -#, fuzzy msgid "Uninstall Template" -msgstr "Desinstalar" +msgstr "Desinstalar Plantilla" #: editor/export_template_manager.cpp msgid "Select Template File" @@ -4062,35 +4058,32 @@ msgid "Collapse All" msgstr "Colapsar Todos" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort files" -msgstr "Buscar archivos" +msgstr "Ordenar archivos" #: editor/filesystem_dock.cpp msgid "Sort by Name (Ascending)" -msgstr "" +msgstr "Ordenar por Nombre (Ascendente)" #: editor/filesystem_dock.cpp msgid "Sort by Name (Descending)" -msgstr "" +msgstr "Ordenar por Nombre (Descendente)" #: editor/filesystem_dock.cpp msgid "Sort by Type (Ascending)" -msgstr "" +msgstr "Ordenar por Tipo (Ascendente)" #: editor/filesystem_dock.cpp msgid "Sort by Type (Descending)" -msgstr "" +msgstr "Ordenar por Tipo (Descendente)" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort by Last Modified" -msgstr "Ultima Modificación" +msgstr "Ordenar por Ultima Modificación" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort by First Modified" -msgstr "Ultima Modificación" +msgstr "Ordenar por Primera Modificación" #: editor/filesystem_dock.cpp msgid "Duplicate..." @@ -4102,7 +4095,7 @@ msgstr "Renombrar..." #: editor/filesystem_dock.cpp msgid "Focus the search box" -msgstr "" +msgstr "Ubicar foco en la caja de búsqueda" #: editor/filesystem_dock.cpp msgid "Previous Folder/File" @@ -4412,14 +4405,12 @@ msgid "Failed to load resource." msgstr "Fallo al cargar recurso." #: editor/inspector_dock.cpp -#, fuzzy msgid "Copy Properties" -msgstr "Propiedades" +msgstr "Copiar Propiedades" #: editor/inspector_dock.cpp -#, fuzzy msgid "Paste Properties" -msgstr "Propiedades" +msgstr "Pegar Propiedades" #: editor/inspector_dock.cpp msgid "Make Sub-Resources Unique" @@ -4444,23 +4435,20 @@ msgid "Save As..." msgstr "Guardar Como..." #: editor/inspector_dock.cpp -#, fuzzy msgid "Extra resource options." -msgstr "No está en la ruta de recursos." +msgstr "Opciones de recursos extra." #: editor/inspector_dock.cpp -#, fuzzy msgid "Edit Resource from Clipboard" -msgstr "Editar Portapapeles de Recursos" +msgstr "Editar Recurso desde el Portapapeles" #: editor/inspector_dock.cpp msgid "Copy Resource" msgstr "Copiar Recurso" #: editor/inspector_dock.cpp -#, fuzzy msgid "Make Resource Built-In" -msgstr "Crear Built-In" +msgstr "Convertir en Recurso Integrado" #: editor/inspector_dock.cpp msgid "Go to the previous edited object in history." @@ -4475,9 +4463,8 @@ msgid "History of recently edited objects." msgstr "Historial de objetos recientemente editados." #: editor/inspector_dock.cpp -#, fuzzy msgid "Open documentation for this object." -msgstr "Abrir Documentación" +msgstr "Abrir la documentación para este objeto." #: editor/inspector_dock.cpp editor/scene_tree_dock.cpp msgid "Open Documentation" @@ -4488,9 +4475,8 @@ msgid "Filter properties" msgstr "Filtrar propiedades" #: editor/inspector_dock.cpp -#, fuzzy msgid "Manage object properties." -msgstr "Propiedades del objeto." +msgstr "Administrar propiedades del objeto." #: editor/inspector_dock.cpp msgid "Changes may be lost!" @@ -4735,9 +4721,8 @@ msgid "Blend:" msgstr "Blend:" #: editor/plugins/animation_blend_tree_editor_plugin.cpp -#, fuzzy msgid "Parameter Changed:" -msgstr "Parámetro Modificado" +msgstr "Parámetro Modificado:" #: editor/plugins/animation_blend_tree_editor_plugin.cpp #: editor/plugins/animation_tree_player_editor_plugin.cpp @@ -5471,11 +5456,11 @@ msgstr "Todos" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Search templates, projects, and demos" -msgstr "" +msgstr "Buscar plantillas, proyectos y demos" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Search assets (excluding templates, projects, and demos)" -msgstr "" +msgstr "Buscar assets (excluyendo plantillas, proyectos y demos)" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Import..." @@ -6124,14 +6109,12 @@ msgid "Clear Pose" msgstr "Restablecer Pose" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Add Node Here" -msgstr "Agregar Nodo" +msgstr "Agregar Nodo Acá" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Instance Scene Here" -msgstr "Instanciar Escena(s)" +msgstr "Instanciar Escena Acá" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Multiply grid step by 2" @@ -6147,49 +6130,43 @@ msgstr "Panear Vista" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 3.125%" -msgstr "" +msgstr "Zoom a 3.125%" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 6.25%" -msgstr "" +msgstr "Zoom a 6.25%" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 12.5%" -msgstr "" +msgstr "Zoom a 12.5%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 25%" -msgstr "Alejar Zoom" +msgstr "Zoom a 25%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 50%" -msgstr "Alejar Zoom" +msgstr "Zoom a 50%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 100%" -msgstr "Alejar Zoom" +msgstr "Zoom a 100%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 200%" -msgstr "Alejar Zoom" +msgstr "Zoom a 200%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 400%" -msgstr "Alejar Zoom" +msgstr "Zoom a 400%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 800%" -msgstr "Alejar Zoom" +msgstr "Zoom a 800%" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 1600%" -msgstr "" +msgstr "Zoom a 1600%" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Add %s" @@ -8734,6 +8711,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Quitar Todos los Ãtems" @@ -8764,6 +8747,12 @@ msgid "Remove All StyleBox Items" msgstr "Quitar Todos los Ãtems" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Agregar Items de Clases" @@ -12188,6 +12177,8 @@ msgid "" "Warning: Having the script name be the same as a built-in type is usually " "not desired." msgstr "" +"Advertencia: Usualmente no se recomienda que un script tenga el mismo nombre " +"que un tipo integrado." #: editor/script_create_dialog.cpp msgid "Class Name:" @@ -12437,6 +12428,16 @@ msgstr "Cambiar Altura de Shape Cilindro" msgid "Change Ray Shape Length" msgstr "Cambiar Largo de Shape Rayo" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "Setear Posición de Punto de Curva" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "Setear Posición de Punto de Curva" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "Cambiar Radio de Cilindro" @@ -14233,6 +14234,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "Este cuerpo será ignorado hasta que se establezca un mesh." @@ -15498,9 +15535,6 @@ msgstr "Las constantes no pueden modificarse." #~ msgid "I see..." #~ msgstr "Ya Veo..." -#~ msgid "Can't open '%s'." -#~ msgstr "No se puede abrir '%s'." - #~ msgid "Ugh" #~ msgstr "Ugh" diff --git a/editor/translations/et.po b/editor/translations/et.po index 6f8561f8ab..13019cd9e3 100644 --- a/editor/translations/et.po +++ b/editor/translations/et.po @@ -340,6 +340,7 @@ msgstr "Muuda animatsiooni silmuse režiimi" msgid "Remove Anim Track" msgstr "Eemalda animatsiooni rada" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Loo uus rada %s-le ja sisesta võti?" @@ -364,10 +365,27 @@ msgstr "Loo" msgid "Anim Insert" msgstr "Animatsiooni sisestus" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animatsioon" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "AnimationPlayer ei saa ennast animeerida, ainult teise mänijaid." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "Atribuut" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Loo ja sisesta animatsioon" @@ -930,7 +948,7 @@ msgstr "" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2261,6 +2279,17 @@ msgid "New Window" msgstr "Uus aken" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Imporditud ressursse ei saa salvestada." @@ -3057,10 +3086,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "" @@ -3673,6 +3698,16 @@ msgid "Download from:" msgstr "" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Ava failihalduris" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "Kopeeri viga" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8388,6 +8423,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Eemalda kõik katkepunktid" @@ -8418,6 +8459,12 @@ msgid "Remove All StyleBox Items" msgstr "Eemalda kõik katkepunktid" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Lisa lemmikutesse" @@ -11868,6 +11915,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13479,6 +13534,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/eu.po b/editor/translations/eu.po index 018f65b79e..7b6934ff33 100644 --- a/editor/translations/eu.po +++ b/editor/translations/eu.po @@ -340,6 +340,7 @@ msgstr "Animazioaren Loop Modua Aldatu" msgid "Remove Anim Track" msgstr "Ezabatu Animazio Pista" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "%s-rentzat pista berria sortu eta giltza sartu?" @@ -364,10 +365,27 @@ msgstr "Sortu" msgid "Anim Insert" msgstr "Animazioa Sartu" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Kargatu animazioa" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "AnimationPlayer bat ezin da norbera animatu, soilik beste playerrak." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "Propietateak" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Animazioa Sortu eta Txertatu" @@ -929,7 +947,7 @@ msgstr "" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2242,6 +2260,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -3025,10 +3054,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "" @@ -3648,6 +3673,15 @@ msgid "Download from:" msgstr "Jaitsiera errorea" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Ireki fitxategi-kudeatzailean" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8355,6 +8389,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Kendu elementu guztiak" @@ -8385,6 +8425,12 @@ msgid "Remove All StyleBox Items" msgstr "Kendu elementu guztiak" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Add Color Item" msgstr "" @@ -11835,6 +11881,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13441,6 +13495,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/fa.po b/editor/translations/fa.po index 09cc83a73b..bb761cf137 100644 --- a/editor/translations/fa.po +++ b/editor/translations/fa.po @@ -357,6 +357,7 @@ msgstr "تغییر ØØ§Ù„ت تکررار (Loop) انیمیشن" msgid "Remove Anim Track" msgstr "ØØ°Ù ترک انیمشین" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "یک ترک جدید برای s% بساز Ùˆ کلید را درج کن؟" @@ -381,10 +382,28 @@ msgstr "تولید" msgid "Anim Insert" msgstr "در انیمیشن درج Ú©Ù†" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "در ØØ§Ù„ اتصال..." + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "انیمیشن" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "انیمیشن پلیر نمی تواند خود را انیمیت کند. Ùقط پلیر دیگر." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "ویژگی '%s' موجود نیست." + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "ساختن Ùˆ درج انیمیشن" @@ -959,7 +978,7 @@ msgstr "ساختن %s جدید" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2282,6 +2301,17 @@ msgid "New Window" msgstr "چارچوب جدید" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -3075,10 +3105,6 @@ msgid "Save & Restart" msgstr "ذخیره Ùˆ خروج" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp #, fuzzy msgid "Update Continuously" msgstr "مستمر" @@ -3722,6 +3748,16 @@ msgid "Download from:" msgstr "خطاهای بارگیری" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "گشودن در مدیر پرونده" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "خطاهای بارگذاری" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8726,6 +8762,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "برداشتن انتخاب شده" @@ -8756,6 +8798,12 @@ msgid "Remove All StyleBox Items" msgstr "برداشتن انتخاب شده" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Ø§ÙØ²ÙˆØ¯Ù† مورد" @@ -12427,6 +12475,16 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "برداشتن موج" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "برداشتن موج" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -14176,6 +14234,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" @@ -14873,10 +14967,6 @@ msgstr "ثوابت قابل تغییر نیستند." #~ msgid "Thanks!" #~ msgstr "با تشکر !" -#, fuzzy -#~ msgid "Can't open '%s'." -#~ msgstr "در ØØ§Ù„ اتصال..." - #~ msgid "Run Script" #~ msgstr "اجرای اسکریپت" diff --git a/editor/translations/fi.po b/editor/translations/fi.po index f0b5ff0457..ffedccec28 100644 --- a/editor/translations/fi.po +++ b/editor/translations/fi.po @@ -16,7 +16,7 @@ msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2021-07-29 21:48+0000\n" +"PO-Revision-Date: 2021-08-10 21:40+0000\n" "Last-Translator: Tapani Niemi <tapani.niemi@kapsi.fi>\n" "Language-Team: Finnish <https://hosted.weblate.org/projects/godot-engine/" "godot/fi/>\n" @@ -25,7 +25,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.7.2-dev\n" +"X-Generator: Weblate 4.8-dev\n" #: core/math/expression.cpp modules/gdscript/gdscript_functions.cpp #: modules/visual_script/visual_script_builtin_funcs.cpp @@ -347,6 +347,7 @@ msgstr "Vaihda animaation toistotilaa" msgid "Remove Anim Track" msgstr "Poista animaatioraita" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Luo kohteelle %s UUSI raita ja lisää avain?" @@ -371,10 +372,28 @@ msgstr "Luo" msgid "Anim Insert" msgstr "Animaatio: lisää" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "Ei voida avata tiedostoa '%s'." + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animaatio" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "AnimationPlayer ei voi animoida itseään, vain muita toistimia." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "Ominaisuutta '%s' ei löytynyt." + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Animaatio: luo ja lisää" @@ -580,9 +599,8 @@ msgid "Go to Previous Step" msgstr "Mene edelliseen askeleeseen" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Apply Reset" -msgstr "Palauta" +msgstr "Tee palautus" #: editor/animation_track_editor.cpp msgid "Optimize Animation" @@ -601,9 +619,8 @@ msgid "Use Bezier Curves" msgstr "Käytä Bezier-käyriä" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Create RESET Track(s)" -msgstr "Liitä raidat" +msgstr "Luo palautusraidat" #: editor/animation_track_editor.cpp msgid "Anim. Optimizer" @@ -927,7 +944,6 @@ msgid "Edit..." msgstr "Muokkaa..." #: editor/connections_dialog.cpp -#, fuzzy msgid "Go to Method" msgstr "Mene metodiin" @@ -947,9 +963,9 @@ msgstr "Luo uusi %s" msgid "No results for \"%s\"." msgstr "Ei tuloksia haulle \"%s\"." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." -msgstr "" +msgstr "%s kuvaus ei ole saatavilla." #: editor/create_dialog.cpp editor/editor_file_dialog.cpp #: editor/filesystem_dock.cpp @@ -1049,18 +1065,16 @@ msgid "Owners Of:" msgstr "Omistajat kohteelle:" #: editor/dependency_editor.cpp -#, fuzzy msgid "" "Remove the selected files from the project? (Cannot be undone.)\n" "Depending on your filesystem configuration, the files will either be moved " "to the system trash or deleted permanently." msgstr "" -"Poista valitut tiedostot projektista? (ei voida kumota)\n" -"Löydät poistetut tiedostot järjestelmän roskakorista, mikäli haluat " -"palauttaa ne." +"Poista valitut tiedostot projektista? (Ei voida kumota.)\n" +"Riippuen tiedostojärjestelmäsi asetuksista, tiedostot siirretään joko " +"järjestelmän roskakoriin tai poistetaan pysyvästi." #: editor/dependency_editor.cpp -#, fuzzy msgid "" "The files being removed are required by other resources in order for them to " "work.\n" @@ -1069,9 +1083,9 @@ msgid "" "to the system trash or deleted permanently." msgstr "" "Poistettavia tiedostoja tarvitaan muiden resurssien toimivuuteen.\n" -"Poistetaanko silti? (ei voida kumota)\n" -"Löydät poistetut tiedostot järjestelmän roskakorista, mikäli haluat " -"palauttaa ne." +"Poistetaanko ne silti? (Ei voida kumota.)\n" +"Riippuen tiedostojärjestelmäsi asetuksista, tiedostot siirretään joko " +"järjestelmän roskakoriin tai poistetaan pysyvästi." #: editor/dependency_editor.cpp msgid "Cannot remove:" @@ -1241,41 +1255,39 @@ msgid "Licenses" msgstr "Lisenssit" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Error opening asset file for \"%s\" (not in ZIP format)." -msgstr "Virhe avattaessa pakettitiedostoa (se ei ole ZIP-muodossa)." +msgstr "Virhe avattaessa \"%s\" asset-tiedostoa (se ei ole ZIP-muodossa)." #: editor/editor_asset_installer.cpp -#, fuzzy msgid "%s (already exists)" msgstr "%s (on jo olemassa)" #: editor/editor_asset_installer.cpp msgid "Contents of asset \"%s\" - %d file(s) conflict with your project:" msgstr "" +"Assetin \"%s\"sisältö - %d tiedostoa on ristiriidassa projektisi kanssa:" #: editor/editor_asset_installer.cpp msgid "Contents of asset \"%s\" - No files conflict with your project:" msgstr "" +"Assetin \"%s\" sisältö - Yksikään tiedosto ei ole ristiriidassa projektisi " +"kanssa:" #: editor/editor_asset_installer.cpp msgid "Uncompressing Assets" msgstr "Puretaan assetteja" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "The following files failed extraction from asset \"%s\":" -msgstr "Seuraavien tiedostojen purku paketista epäonnistui:" +msgstr "Seuraavien tiedostojen purku assetista \"%s\" epäonnistui:" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "(and %s more files)" -msgstr "Ja vielä %s tiedostoa." +msgstr "(ja vielä %s tiedostoa)" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Asset \"%s\" installed successfully!" -msgstr "Paketti asennettu onnistuneesti!" +msgstr "Asset \"%s\" asennettu onnistuneesti!" #: editor/editor_asset_installer.cpp #: editor/plugins/asset_library_editor_plugin.cpp @@ -1287,9 +1299,8 @@ msgid "Install" msgstr "Asenna" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Asset Installer" -msgstr "Pakettien asentaja" +msgstr "Assettien asentaja" #: editor/editor_audio_buses.cpp msgid "Speakers" @@ -1352,7 +1363,6 @@ msgid "Bypass" msgstr "Ohita" #: editor/editor_audio_buses.cpp -#, fuzzy msgid "Bus Options" msgstr "Väylän asetukset" @@ -1523,13 +1533,12 @@ msgid "Can't add autoload:" msgstr "Ei voida lisätä automaattisesti ladattavaa:" #: editor/editor_autoload_settings.cpp -#, fuzzy msgid "%s is an invalid path. File does not exist." -msgstr "Tiedostoa ei ole olemassa." +msgstr "%s on virheellinen polku. Tiedostoa ei ole olemassa." #: editor/editor_autoload_settings.cpp msgid "%s is an invalid path. Not in resource path (res://)." -msgstr "" +msgstr "%s on virheellinen polku. Ei löydy resurssipolusta (res://)." #: editor/editor_autoload_settings.cpp msgid "Add AutoLoad" @@ -1553,9 +1562,8 @@ msgid "Name" msgstr "Nimi" #: editor/editor_autoload_settings.cpp -#, fuzzy msgid "Global Variable" -msgstr "Muuttuja" +msgstr "Globaali muuttuja" #: editor/editor_data.cpp msgid "Paste Params" @@ -1728,48 +1736,51 @@ msgstr "Tuontitelakka" #: editor/editor_feature_profile.cpp msgid "Allows to view and edit 3D scenes." -msgstr "" +msgstr "Antaa katsella ja muokata 3D-skenejä." #: editor/editor_feature_profile.cpp msgid "Allows to edit scripts using the integrated script editor." -msgstr "" +msgstr "Antaa muokata skriptejä käyttäen integroitua skriptieditoria." #: editor/editor_feature_profile.cpp msgid "Provides built-in access to the Asset Library." -msgstr "" +msgstr "Tarjoaa valmiin pääsyn Asset-kirjastoon." #: editor/editor_feature_profile.cpp msgid "Allows editing the node hierarchy in the Scene dock." -msgstr "" +msgstr "Antaa muokata solmuhierarkiaa Skene-telakassa." #: editor/editor_feature_profile.cpp msgid "" "Allows to work with signals and groups of the node selected in the Scene " "dock." msgstr "" +"Antaa työskennellä valitun solmun signaalien ja ryhmien kanssa Skene-" +"telakassa." #: editor/editor_feature_profile.cpp msgid "Allows to browse the local file system via a dedicated dock." -msgstr "" +msgstr "Antaa selata paikallista tiedostojärjestelmää erillisellä telakalla." #: editor/editor_feature_profile.cpp msgid "" "Allows to configure import settings for individual assets. Requires the " "FileSystem dock to function." msgstr "" +"Antaa konfiguroida tuontiasetuksia yksittäiselle assetille. Edellyttää " +"Tiedostojärjestelmä-telakkaa toimiakseen." #: editor/editor_feature_profile.cpp -#, fuzzy msgid "(current)" -msgstr "(Nykyinen)" +msgstr "(nykyinen)" #: editor/editor_feature_profile.cpp msgid "(none)" -msgstr "" +msgstr "(ei mikään)" #: editor/editor_feature_profile.cpp msgid "Remove currently selected profile, '%s'? Cannot be undone." -msgstr "" +msgstr "Poistetaanko nykyinen valittu profiili, '%s'? Ei voida kumota." #: editor/editor_feature_profile.cpp msgid "Profile must be a valid filename and must not contain '.'" @@ -1802,19 +1813,16 @@ msgid "Enable Contextual Editor" msgstr "Ota asiayhteydellinen editori käyttöön" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Class Properties:" -msgstr "Ominaisuudet:" +msgstr "Luokan ominaisuudet:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Main Features:" -msgstr "Ominaisuudet" +msgstr "Pääominaisuudet:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Nodes and Classes:" -msgstr "Käytössä olevat luokat:" +msgstr "Solmut ja luokat:" #: editor/editor_feature_profile.cpp msgid "File '%s' format is invalid, import aborted." @@ -1833,7 +1841,6 @@ msgid "Error saving profile to path: '%s'." msgstr "Virhe tallennettaessa profiilia polkuun: '%s'." #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Reset to Default" msgstr "Palauta oletusarvoihin" @@ -1842,14 +1849,12 @@ msgid "Current Profile:" msgstr "Nykyinen profiili:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Create Profile" -msgstr "Tyhjennä profiili" +msgstr "Luo profiili" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Remove Profile" -msgstr "Poista laatta" +msgstr "Poista profiili" #: editor/editor_feature_profile.cpp msgid "Available Profiles:" @@ -1869,18 +1874,17 @@ msgid "Export" msgstr "Vie" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Configure Selected Profile:" -msgstr "Nykyinen profiili:" +msgstr "Konfiguroi valittu profiili:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Extra Options:" -msgstr "Tekstuurin asetukset" +msgstr "Ylimääräiset asetukset:" #: editor/editor_feature_profile.cpp msgid "Create or import a profile to edit available classes and properties." msgstr "" +"Luo tai tuo profiili muokataksesi saatavilla olevia luokkia ja ominaisuuksia." #: editor/editor_feature_profile.cpp msgid "New profile name:" @@ -1907,9 +1911,8 @@ msgid "Select Current Folder" msgstr "Valitse nykyinen kansio" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp -#, fuzzy msgid "File exists, overwrite?" -msgstr "Tiedosto on jo olemassa, korvaa?" +msgstr "Tiedosto on jo olemassa, korvataanko?" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp msgid "Select This Folder" @@ -2301,6 +2304,17 @@ msgid "New Window" msgstr "Uusi ikkuna" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "Pyörii kun editorin ikkuna päivittyy." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Tuotuja resursseja ei voida tallentaa." @@ -2530,13 +2544,16 @@ msgid "" "The current scene has no root node, but %d modified external resource(s) " "were saved anyway." msgstr "" +"Nykyisellä skenellä ei ole juurisolmua, mutta %d muokattua ulkoista " +"resurssia tallennettiin silti." #: editor/editor_node.cpp -#, fuzzy msgid "" "A root node is required to save the scene. You can add a root node using the " "Scene tree dock." -msgstr "Skenen tallentaminen edellyttää, että sillä on juurisolmu." +msgstr "" +"Skenen tallentaminen edellyttää, että sillä on juurisolmu. Voit lisätä " +"juurisolmun Skene-telakasta." #: editor/editor_node.cpp msgid "Save Scene As..." @@ -2914,9 +2931,8 @@ msgid "Orphan Resource Explorer..." msgstr "Irrallisten resurssien hallinta..." #: editor/editor_node.cpp -#, fuzzy msgid "Reload Current Project" -msgstr "Nimetä projekti" +msgstr "Lataa uudelleen nykyinen projekti" #: editor/editor_node.cpp msgid "Quit to Project List" @@ -3076,22 +3092,20 @@ msgid "Help" msgstr "Ohje" #: editor/editor_node.cpp -#, fuzzy msgid "Online Documentation" -msgstr "Avaa dokumentaatio" +msgstr "Online-dokumentaatio" #: editor/editor_node.cpp msgid "Questions & Answers" -msgstr "" +msgstr "Kysymykset & vastaukset" #: editor/editor_node.cpp msgid "Report a Bug" msgstr "Raportoi bugi" #: editor/editor_node.cpp -#, fuzzy msgid "Suggest a Feature" -msgstr "Aseta arvo" +msgstr "Ehdota ominaisuutta" #: editor/editor_node.cpp msgid "Send Docs Feedback" @@ -3102,9 +3116,8 @@ msgid "Community" msgstr "Yhteisö" #: editor/editor_node.cpp -#, fuzzy msgid "About Godot" -msgstr "Tietoja" +msgstr "Tietoja Godotista" #: editor/editor_node.cpp msgid "Support Godot Development" @@ -3156,10 +3169,6 @@ msgid "Save & Restart" msgstr "Tallenna & käynnistä uudelleen" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "Pyörii kun editorin ikkuna päivittyy." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "Päivitä jatkuvasti" @@ -3200,14 +3209,12 @@ msgid "Manage Templates" msgstr "Hallinnoi malleja" #: editor/editor_node.cpp -#, fuzzy msgid "Install from file" msgstr "Asenna tiedostosta" #: editor/editor_node.cpp -#, fuzzy msgid "Select android sources file" -msgstr "Valitse lähdemesh:" +msgstr "Valitse android-lähdetiedosto" #: editor/editor_node.cpp msgid "" @@ -3290,9 +3297,8 @@ msgid "Select" msgstr "Valitse" #: editor/editor_node.cpp -#, fuzzy msgid "Select Current" -msgstr "Valitse nykyinen kansio" +msgstr "Valitse nykyinen" #: editor/editor_node.cpp msgid "Open 2D Editor" @@ -3327,9 +3333,8 @@ msgid "No sub-resources found." msgstr "Aliresursseja ei löydetty." #: editor/editor_path.cpp -#, fuzzy msgid "Open a list of sub-resources." -msgstr "Aliresursseja ei löydetty." +msgstr "Avaa aliresurssien luettelo." #: editor/editor_plugin.cpp msgid "Creating Mesh Previews" @@ -3356,14 +3361,12 @@ msgid "Update" msgstr "Päivitä" #: editor/editor_plugin_settings.cpp -#, fuzzy msgid "Version" -msgstr "Versio:" +msgstr "Versio" #: editor/editor_plugin_settings.cpp -#, fuzzy msgid "Author" -msgstr "Tekijät" +msgstr "Tekijä" #: editor/editor_plugin_settings.cpp #: editor/plugins/version_control_editor_plugin.cpp @@ -3376,14 +3379,12 @@ msgid "Measure:" msgstr "Mittaa:" #: editor/editor_profiler.cpp -#, fuzzy msgid "Frame Time (ms)" -msgstr "Kuvaruudun aika (sek)" +msgstr "Kuvaruudun aika (ms)" #: editor/editor_profiler.cpp -#, fuzzy msgid "Average Time (ms)" -msgstr "Keskimääräinen aika (sek)" +msgstr "Keskimääräinen aika (ms)" #: editor/editor_profiler.cpp msgid "Frame %" @@ -3410,6 +3411,13 @@ msgid "" "functions called by that function.\n" "Use this to find individual functions to optimize." msgstr "" +"Sisältävä: Sisältää muissa tämän funktion kutsumissa funktioissa kuluneen " +"ajan.\n" +"Käytä tätä löytääksesi pullonkaulat.\n" +"\n" +"Itse: Lasketaan ainoastaan funktiossa itsessään kulunut aika, eikä muissa " +"tuon funktion kutsumissa funktioissa käytettyä aikaa.\n" +"Käytä tätä löytääksesi yksittäiset optimointia vaativat funktiot." #: editor/editor_profiler.cpp msgid "Frame #:" @@ -3532,7 +3540,6 @@ msgid "Paste" msgstr "Liitä" #: editor/editor_resource_picker.cpp editor/property_editor.cpp -#, fuzzy msgid "Convert to %s" msgstr "Muunna muotoon %s" @@ -3625,31 +3632,27 @@ msgstr "Noudetaan peilipalvelimia, hetkinen..." #: editor/export_template_manager.cpp msgid "Starting the download..." -msgstr "" +msgstr "Aloitetaan lataus..." #: editor/export_template_manager.cpp msgid "Error requesting URL:" msgstr "Virhe pyydettäessä osoitetta:" #: editor/export_template_manager.cpp -#, fuzzy msgid "Connecting to the mirror..." msgstr "Yhdistetään peilipalvelimeen..." #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't resolve the requested address." -msgstr "Palvelinta ei löytynyt:" +msgstr "Pyydetyn osoitteen selvitys ei onnistunut." #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't connect to the mirror." -msgstr "Isäntään yhdistäminen epäonnistui:" +msgstr "Peilipalvelimeen yhdistäminen epäonnistui." #: editor/export_template_manager.cpp -#, fuzzy msgid "No response from the mirror." -msgstr "Ei vastausta isännältä:" +msgstr "Ei vastausta peilipalvelimelta." #: editor/export_template_manager.cpp #: editor/plugins/asset_library_editor_plugin.cpp @@ -3657,18 +3660,16 @@ msgid "Request failed." msgstr "Pyyntö epäonnistui." #: editor/export_template_manager.cpp -#, fuzzy msgid "Request ended up in a redirect loop." -msgstr "Pyyntö epäonnistui, liikaa uudelleenohjauksia" +msgstr "Pyyntö päätyi uudelleenohjaussilmukkaan." #: editor/export_template_manager.cpp -#, fuzzy msgid "Request failed:" -msgstr "Pyyntö epäonnistui." +msgstr "Pyyntö epäonnistui:" #: editor/export_template_manager.cpp msgid "Download complete; extracting templates..." -msgstr "" +msgstr "Lataaminen valmis; puretaan malleja..." #: editor/export_template_manager.cpp msgid "Cannot remove temporary file:" @@ -3695,7 +3696,7 @@ msgstr "" #: editor/export_template_manager.cpp msgid "Best available mirror" -msgstr "" +msgstr "Paras saatavilla oleva peilipalvelin" #: editor/export_template_manager.cpp msgid "" @@ -3748,9 +3749,8 @@ msgid "SSL Handshake Error" msgstr "Virhe SSL kättelyssä" #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't open the export templates file." -msgstr "Vientimallien zip-tiedostoa ei voitu avata." +msgstr "Vientimallien tiedostoa ei voida avata." #: editor/export_template_manager.cpp #, fuzzy @@ -3801,9 +3801,8 @@ msgid "Export templates are installed and ready to be used." msgstr "" #: editor/export_template_manager.cpp -#, fuzzy msgid "Open Folder" -msgstr "Avaa tiedosto" +msgstr "Avaa kansio" #: editor/export_template_manager.cpp msgid "Open the folder containing installed templates for the current version." @@ -3819,33 +3818,42 @@ msgid "Uninstall templates for the current version." msgstr "Laskurin alkuarvo" #: editor/export_template_manager.cpp -#, fuzzy msgid "Download from:" -msgstr "Latausvirhe" +msgstr "Lataa sijannista:" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Suorita selaimessa" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "Kopioi virhe" #: editor/export_template_manager.cpp msgid "Download and Install" -msgstr "" +msgstr "Lataa ja asenna" #: editor/export_template_manager.cpp msgid "" "Download and install templates for the current version from the best " "possible mirror." msgstr "" +"Lataa ja asenna mallit nykyiselle versiolle parhaasta mahdollisesta " +"peilipalvelimesta." #: editor/export_template_manager.cpp msgid "Official export templates aren't available for development builds." msgstr "Viralliset vientimallit eivät ole saatavilla kehityskäännöksille." #: editor/export_template_manager.cpp -#, fuzzy msgid "Install from File" msgstr "Asenna tiedostosta" #: editor/export_template_manager.cpp -#, fuzzy msgid "Install templates from a local file." -msgstr "Tuo mallit ZIP-tiedostosta" +msgstr "Asenna mallit paikallisesta tiedostosta." #: editor/export_template_manager.cpp editor/find_in_files.cpp #: editor/progress_dialog.cpp scene/gui/dialogs.cpp @@ -3853,19 +3861,16 @@ msgid "Cancel" msgstr "Peruuta" #: editor/export_template_manager.cpp -#, fuzzy msgid "Cancel the download of the templates." -msgstr "Vientimallien zip-tiedostoa ei voitu avata." +msgstr "Keskeytä mallien lataus." #: editor/export_template_manager.cpp -#, fuzzy msgid "Other Installed Versions:" -msgstr "Asennetut versiot:" +msgstr "Muut asennetut versiot:" #: editor/export_template_manager.cpp -#, fuzzy msgid "Uninstall Template" -msgstr "Poista asennus" +msgstr "Poista malli" #: editor/export_template_manager.cpp msgid "Select Template File" @@ -3880,6 +3885,8 @@ msgid "" "The templates will continue to download.\n" "You may experience a short editor freeze when they finish." msgstr "" +"Mallien lataus jatkuu.\n" +"Saatat kokea lyhyitä editorin jähmettymisiä niiden tullessa valmiiksi." #: editor/filesystem_dock.cpp msgid "Favorites" @@ -4026,35 +4033,32 @@ msgid "Collapse All" msgstr "Tiivistä kaikki" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort files" -msgstr "Etsi tiedostoista" +msgstr "Lajittele tiedostot" #: editor/filesystem_dock.cpp msgid "Sort by Name (Ascending)" -msgstr "" +msgstr "Lajittele nimen mukaan (nouseva)" #: editor/filesystem_dock.cpp msgid "Sort by Name (Descending)" -msgstr "" +msgstr "Lajittele nimen mukaan (laskeva)" #: editor/filesystem_dock.cpp msgid "Sort by Type (Ascending)" -msgstr "" +msgstr "Lajittele tyypin mukaan (nouseva)" #: editor/filesystem_dock.cpp msgid "Sort by Type (Descending)" -msgstr "" +msgstr "Lajittele tyypin mukaan (laskeva)" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort by Last Modified" -msgstr "Viimeksi muutettu" +msgstr "Lajittele viimeksi muokatun mukaan" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort by First Modified" -msgstr "Viimeksi muutettu" +msgstr "Lajittele ensiksi muokatun mukaan" #: editor/filesystem_dock.cpp msgid "Duplicate..." @@ -4378,14 +4382,12 @@ msgid "Failed to load resource." msgstr "Resurssin lataaminen epäonnistui." #: editor/inspector_dock.cpp -#, fuzzy msgid "Copy Properties" -msgstr "Ominaisuudet" +msgstr "Kopioi ominaisuudet" #: editor/inspector_dock.cpp -#, fuzzy msgid "Paste Properties" -msgstr "Ominaisuudet" +msgstr "Liitä ominaisuudet" #: editor/inspector_dock.cpp msgid "Make Sub-Resources Unique" @@ -4410,9 +4412,8 @@ msgid "Save As..." msgstr "Tallenna nimellä..." #: editor/inspector_dock.cpp -#, fuzzy msgid "Extra resource options." -msgstr "Ei löytynyt resurssipolusta." +msgstr "Ylimääräiset resurssivalinnat." #: editor/inspector_dock.cpp #, fuzzy @@ -5437,7 +5438,7 @@ msgstr "" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Search assets (excluding templates, projects, and demos)" -msgstr "" +msgstr "Etsi assetteja (poislukien mallit, projektit ja demot)" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Import..." @@ -5481,7 +5482,7 @@ msgstr "Assettien zip-tiedosto" #: editor/plugins/audio_stream_editor_plugin.cpp msgid "Audio Preview Play/Pause" -msgstr "" +msgstr "Äänen esikuuntelun toisto/keskeytys" #: editor/plugins/baked_lightmap_editor_plugin.cpp msgid "" @@ -5741,13 +5742,12 @@ msgstr "Muuta ankkureita" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "" "Project Camera Override\n" "Overrides the running project's camera with the editor viewport camera." msgstr "" -"Pelikameran ohitus\n" -"Ohittaa pelikameran editorin näyttöruutukameralla." +"Projektikameran ohitus\n" +"Ohittaa käynnissä olevan projektin kameran editorin näyttöruutukameralla." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp @@ -5756,6 +5756,9 @@ msgid "" "No project instance running. Run the project from the editor to use this " "feature." msgstr "" +"Projektikameran ohitus\n" +"Mikään projekti ei ole käynnissä. Aja projekti editorista käyttääksesi tätä " +"ominaisuutta." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp @@ -8692,6 +8695,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Poista kaikki" @@ -8722,6 +8731,12 @@ msgid "Remove All StyleBox Items" msgstr "Poista kaikki" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Lisää luokka" @@ -12384,6 +12399,16 @@ msgstr "Muuta sylinterimuodon korkeutta" msgid "Change Ray Shape Length" msgstr "Vaihda säteen muodon pituutta" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "Aseta käyräpisteen sijainti" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "Aseta käyräpisteen sijainti" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "Muuta sylinterin sädettä" @@ -14163,6 +14188,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "Tämä kappale sivuutetaan, kunnes asetat meshin." @@ -15490,9 +15551,6 @@ msgstr "Vakioita ei voi muokata." #~ msgid "I see..." #~ msgstr "Ymmärrän..." -#~ msgid "Can't open '%s'." -#~ msgstr "Ei voida avata tiedostoa '%s'." - #~ msgid "Ugh" #~ msgstr "Äh" diff --git a/editor/translations/fil.po b/editor/translations/fil.po index aacdf9c442..e53b7bb1a7 100644 --- a/editor/translations/fil.po +++ b/editor/translations/fil.po @@ -345,6 +345,7 @@ msgstr "" msgid "Remove Anim Track" msgstr "" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "" @@ -369,10 +370,26 @@ msgstr "Lumikha" msgid "Anim Insert" msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Pagulit ng Animation" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "property '%s'" +msgstr "" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "" @@ -928,7 +945,7 @@ msgstr "" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2222,6 +2239,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -3002,10 +3030,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp #, fuzzy msgid "Update Continuously" msgstr "Tuloy-tuloy" @@ -3615,6 +3639,14 @@ msgid "Download from:" msgstr "Kumpleto ang pag-Download." #: editor/export_template_manager.cpp +msgid "Open in Web Browser" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8303,6 +8335,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Remove All Color Items" msgstr "" @@ -8329,6 +8367,12 @@ msgid "Remove All StyleBox Items" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Add Color Item" msgstr "" @@ -11749,6 +11793,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13350,6 +13402,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/fr.po b/editor/translations/fr.po index 9be7d406dd..e6e2c9021e 100644 --- a/editor/translations/fr.po +++ b/editor/translations/fr.po @@ -42,7 +42,7 @@ # Xananax <xananax@yelostudio.com>, 2017-2018. # Perrier Mathis <mathis.perrier73@gmail.com>, 2018. # Ewan Lehnebach <ewan.lehnebach@gmail.com>, 2018. -# Hugo Locurcio <hugo.locurcio@hugo.pro>, 2018, 2019, 2020. +# Hugo Locurcio <hugo.locurcio@hugo.pro>, 2018, 2019, 2020, 2021. # Grigore Antoniuc <grisa181@gmail.com>, 2018. # x2f <x.defoy@gmail.com>, 2018. # LittleWhite <lw.demoscene@googlemail.com>, 2018. @@ -81,12 +81,14 @@ # ASTRALE <jules.cercy@etu.univ-lyon1.fr>, 2021. # Julien Vanelian <julienvanelian@hotmail.com>, 2021. # Clément Topy <topy72.mine@gmail.com>, 2021. +# Cold <coldragon78@gmail.com>, 2021. +# Blackiris <divjvc@free.fr>, 2021. msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2021-07-16 05:47+0000\n" -"Last-Translator: Pierre Caye <pierrecaye@laposte.net>\n" +"PO-Revision-Date: 2021-08-12 14:48+0000\n" +"Last-Translator: Blackiris <divjvc@free.fr>\n" "Language-Team: French <https://hosted.weblate.org/projects/godot-engine/" "godot/fr/>\n" "Language: fr\n" @@ -94,7 +96,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" -"X-Generator: Weblate 4.7.2-dev\n" +"X-Generator: Weblate 4.8-dev\n" #: core/math/expression.cpp modules/gdscript/gdscript_functions.cpp #: modules/visual_script/visual_script_builtin_funcs.cpp @@ -416,6 +418,7 @@ msgstr "Modifier le mode de boucle d’animation" msgid "Remove Anim Track" msgstr "Supprimer la piste d’animation" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Créer une NOUVELLE piste pour %s et insérer une clé ?" @@ -440,11 +443,29 @@ msgstr "Créer" msgid "Anim Insert" msgstr "Insérer une animation" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "Mode d'aimantation (%s)" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animation" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" "Un AnimationPlayer ne peut s’animer lui-même, seulement les autres lecteurs." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "Il n'y a pas de propriété « %s »." + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Créer et insérer une animation" @@ -660,9 +681,8 @@ msgid "Go to Previous Step" msgstr "Aller à l'étape précédente" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Apply Reset" -msgstr "Réinitialiser" +msgstr "Appliquer la réinitialisation" #: editor/animation_track_editor.cpp msgid "Optimize Animation" @@ -681,9 +701,8 @@ msgid "Use Bezier Curves" msgstr "Utiliser les courbes de Bézier" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Create RESET Track(s)" -msgstr "Coller pistes" +msgstr "Créer des pistes RESET" #: editor/animation_track_editor.cpp msgid "Anim. Optimizer" @@ -1008,7 +1027,6 @@ msgid "Edit..." msgstr "Édition..." #: editor/connections_dialog.cpp -#, fuzzy msgid "Go to Method" msgstr "Aller à la méthode" @@ -1028,9 +1046,9 @@ msgstr "Créer un nouveau %s" msgid "No results for \"%s\"." msgstr "Aucun résultats pour \"%s\"." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." -msgstr "" +msgstr "Pas de description disponible pour %s." #: editor/create_dialog.cpp editor/editor_file_dialog.cpp #: editor/filesystem_dock.cpp @@ -1130,18 +1148,16 @@ msgid "Owners Of:" msgstr "Propriétaires de :" #: editor/dependency_editor.cpp -#, fuzzy msgid "" "Remove the selected files from the project? (Cannot be undone.)\n" "Depending on your filesystem configuration, the files will either be moved " "to the system trash or deleted permanently." msgstr "" -"Supprimer les fichiers sélectionnés du projet ? (annulation impossible)\n" -"Vous pouvez retrouver les fichiers supprimés dans la corbeille du système " -"pour les restaurer." +"Supprimer les fichiers sélectionnés du projet ? (Annulation impossible.)\n" +"En fonction de la configuration de votre système, les fichiers seront soient " +"déplacés vers la corbeille du système, soit supprimés définitivement." #: editor/dependency_editor.cpp -#, fuzzy msgid "" "The files being removed are required by other resources in order for them to " "work.\n" @@ -1151,9 +1167,9 @@ msgid "" msgstr "" "Les fichiers qui vont être supprimés sont utilisés par d'autres ressources " "pour leur fonctionnement.\n" -"Les supprimer tout de même ? (annulation impossible)\n" -"Vous pouvez retrouver les fichiers supprimés dans la corbeille du système " -"pour les restaurer." +"Les supprimer tout de même ? (Annulation impossible.)\n" +"En fonction de la configuration de votre système, les fichiers seront soient " +"déplacés vers la corbeille du système, soit supprimés définitivement." #: editor/dependency_editor.cpp msgid "Cannot remove:" @@ -1323,42 +1339,41 @@ msgid "Licenses" msgstr "Licences" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Error opening asset file for \"%s\" (not in ZIP format)." msgstr "" -"Erreur lors de l'ouverture du fichier package (il n'est pas au format ZIP)." +"Erreur lors de l'ouverture du fichier d'asset « %s » (il n'est pas au format " +"ZIP)." #: editor/editor_asset_installer.cpp -#, fuzzy msgid "%s (already exists)" msgstr "%s (existe déjà )" #: editor/editor_asset_installer.cpp msgid "Contents of asset \"%s\" - %d file(s) conflict with your project:" msgstr "" +"Contenus de l'asset « %s » - %d fichier(s) sont en conflit avec votre " +"projet :" #: editor/editor_asset_installer.cpp msgid "Contents of asset \"%s\" - No files conflict with your project:" msgstr "" +"Contenus de l'asset « %s » - Pas de fichiers en conflit avec votre projet :" #: editor/editor_asset_installer.cpp msgid "Uncompressing Assets" msgstr "Décompression des assets" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "The following files failed extraction from asset \"%s\":" -msgstr "L'extraction des fichiers suivants depuis le paquetage a échoué :" +msgstr "L'extraction des fichiers suivants depuis l'asset « %s » a échoué :" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "(and %s more files)" -msgstr "Et %s fichiers supplémentaires." +msgstr "(et %s fichiers supplémentaires)" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Asset \"%s\" installed successfully!" -msgstr "Paquetage installé avec succès !" +msgstr "Asset « %s » installé avec succès !" #: editor/editor_asset_installer.cpp #: editor/plugins/asset_library_editor_plugin.cpp @@ -1370,9 +1385,8 @@ msgid "Install" msgstr "Installer" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Asset Installer" -msgstr "Installeur de paquetage" +msgstr "Installateur d'asset" #: editor/editor_audio_buses.cpp msgid "Speakers" @@ -1435,7 +1449,6 @@ msgid "Bypass" msgstr "Contourner" #: editor/editor_audio_buses.cpp -#, fuzzy msgid "Bus Options" msgstr "Options de bus" @@ -1605,28 +1618,29 @@ msgid "Can't add autoload:" msgstr "Impossible d'ajouter le chargement automatique :" #: editor/editor_autoload_settings.cpp -#, fuzzy msgid "%s is an invalid path. File does not exist." -msgstr "Le fichier n'existe pas." +msgstr "%s est un chemin invalide. Le fichier n'existe pas." #: editor/editor_autoload_settings.cpp msgid "%s is an invalid path. Not in resource path (res://)." msgstr "" +"%s est un chemin invalide. Il n'est pas dans le chemin des ressources " +"(res://)." #: editor/editor_autoload_settings.cpp msgid "Add AutoLoad" -msgstr "Ajouter le chargement automatique" +msgstr "Ajouter chargement automatique" #: editor/editor_autoload_settings.cpp editor/editor_file_dialog.cpp #: editor/editor_plugin_settings.cpp #: editor/plugins/animation_tree_editor_plugin.cpp #: editor/script_create_dialog.cpp scene/gui/file_dialog.cpp msgid "Path:" -msgstr "Chemin :" +msgstr "Chemin :" #: editor/editor_autoload_settings.cpp msgid "Node Name:" -msgstr "Nom de nÅ“ud :" +msgstr "Nom de nÅ“ud :" #: editor/editor_autoload_settings.cpp editor/editor_help_search.cpp #: editor/editor_plugin_settings.cpp editor/editor_profiler.cpp @@ -1635,9 +1649,8 @@ msgid "Name" msgstr "Nom" #: editor/editor_autoload_settings.cpp -#, fuzzy msgid "Global Variable" -msgstr "Renommer la variable" +msgstr "Variable globale" #: editor/editor_data.cpp msgid "Paste Params" @@ -1811,48 +1824,55 @@ msgstr "Dock d'importation" #: editor/editor_feature_profile.cpp msgid "Allows to view and edit 3D scenes." -msgstr "" +msgstr "Permet de visualiser et modifier des scènes 3D." #: editor/editor_feature_profile.cpp msgid "Allows to edit scripts using the integrated script editor." msgstr "" +"Permet de modifier des scripts à l'aide de l'éditeur de script intégré." #: editor/editor_feature_profile.cpp msgid "Provides built-in access to the Asset Library." -msgstr "" +msgstr "Fournit un accès à l'Asset Library." #: editor/editor_feature_profile.cpp msgid "Allows editing the node hierarchy in the Scene dock." -msgstr "" +msgstr "Permet de visualiser la hiérarchie des nÅ“uds dans le dock Scène." #: editor/editor_feature_profile.cpp msgid "" "Allows to work with signals and groups of the node selected in the Scene " "dock." msgstr "" +"Permet de travailler avec les signaux et groupes d'un nÅ“ud sélectionné dans " +"le dock Scène." #: editor/editor_feature_profile.cpp msgid "Allows to browse the local file system via a dedicated dock." msgstr "" +"Permet de naviguer le système de fichiers local à l'aide d'un dock dédié." #: editor/editor_feature_profile.cpp msgid "" "Allows to configure import settings for individual assets. Requires the " "FileSystem dock to function." msgstr "" +"Permet de configurer les paramètres d'importation pour des ressources " +"individuelles. Nécessite le dock Système de fichiers pour fonctionner." #: editor/editor_feature_profile.cpp -#, fuzzy msgid "(current)" -msgstr "(Actuel)" +msgstr "(actuel)" #: editor/editor_feature_profile.cpp msgid "(none)" -msgstr "" +msgstr "(aucun)" #: editor/editor_feature_profile.cpp msgid "Remove currently selected profile, '%s'? Cannot be undone." msgstr "" +"Supprimer le profil actuellement sélectionné « %s » ? Cette suppression ne " +"peut être annulée." #: editor/editor_feature_profile.cpp msgid "Profile must be a valid filename and must not contain '.'" @@ -1884,19 +1904,16 @@ msgid "Enable Contextual Editor" msgstr "Ouvrir l'éditeur contextuel" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Class Properties:" -msgstr "Propriétés :" +msgstr "Propriétés de la classe :" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Main Features:" -msgstr "Fonctionnalités" +msgstr "Fonctionnalités principales :" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Nodes and Classes:" -msgstr "Classes activées :" +msgstr "NÅ“uds et classes :" #: editor/editor_feature_profile.cpp msgid "File '%s' format is invalid, import aborted." @@ -1915,23 +1932,20 @@ msgid "Error saving profile to path: '%s'." msgstr "Erreur lors de l'enregistrement du profil au chemin : « %s »." #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Reset to Default" -msgstr "Réinitialiser" +msgstr "Réinitialiser aux valeurs par défaut" #: editor/editor_feature_profile.cpp msgid "Current Profile:" msgstr "Profil actuel :" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Create Profile" -msgstr "Effacer le profil" +msgstr "Créer un profil" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Remove Profile" -msgstr "Supprimer la tuile" +msgstr "Supprimer le profil" #: editor/editor_feature_profile.cpp msgid "Available Profiles:" @@ -1951,18 +1965,18 @@ msgid "Export" msgstr "Exporter" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Configure Selected Profile:" -msgstr "Profil actuel :" +msgstr "Configurer le profil sélectionné :" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Extra Options:" -msgstr "Options de classe :" +msgstr "Options additionnelles :" #: editor/editor_feature_profile.cpp msgid "Create or import a profile to edit available classes and properties." msgstr "" +"Créer ou importer un profil pour modifier les classes et propriétés " +"disponibles." #: editor/editor_feature_profile.cpp msgid "New profile name:" @@ -1989,9 +2003,8 @@ msgid "Select Current Folder" msgstr "Sélectionner le dossier courant" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp -#, fuzzy msgid "File exists, overwrite?" -msgstr "Le fichier existe, l'écraser ?" +msgstr "Le fichier existe, l'écraser ?" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp msgid "Select This Folder" @@ -2384,6 +2397,17 @@ msgid "New Window" msgstr "Nouvelle Fenêtre" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "Tourne lorsque la fenêtre de l'éditeur est redessinée." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Les ressources importées ne peuvent pas être sauvegardées." @@ -2620,13 +2644,16 @@ msgid "" "The current scene has no root node, but %d modified external resource(s) " "were saved anyway." msgstr "" +"La scène actuelle n'a pas de nÅ“ud racine, mais %d ressources externes " +"modifiées ont tout de même été enregistrées." #: editor/editor_node.cpp -#, fuzzy msgid "" "A root node is required to save the scene. You can add a root node using the " "Scene tree dock." -msgstr "Un nÅ“ud racine est nécessaire pour sauvegarder la scène." +msgstr "" +"Un nÅ“ud racine est nécessaire pour enregistrer la scène. Vous pouvez ajouter " +"un nÅ“ud racine en utilisant le dock Scène." #: editor/editor_node.cpp msgid "Save Scene As..." @@ -3018,9 +3045,8 @@ msgid "Orphan Resource Explorer..." msgstr "Explorateur de ressources orphelines..." #: editor/editor_node.cpp -#, fuzzy msgid "Reload Current Project" -msgstr "Renommer le projet" +msgstr "Recharger le projet actuel" #: editor/editor_node.cpp msgid "Quit to Project List" @@ -3183,13 +3209,12 @@ msgid "Help" msgstr "Aide" #: editor/editor_node.cpp -#, fuzzy msgid "Online Documentation" -msgstr "Ouvrir la documentation" +msgstr "Documentation en ligne" #: editor/editor_node.cpp msgid "Questions & Answers" -msgstr "" +msgstr "Questions et réponses" #: editor/editor_node.cpp msgid "Report a Bug" @@ -3197,7 +3222,7 @@ msgstr "Signaler un bug" #: editor/editor_node.cpp msgid "Suggest a Feature" -msgstr "" +msgstr "Suggérer une fonctionnalité" #: editor/editor_node.cpp msgid "Send Docs Feedback" @@ -3208,9 +3233,8 @@ msgid "Community" msgstr "Communauté" #: editor/editor_node.cpp -#, fuzzy msgid "About Godot" -msgstr "À propos" +msgstr "À propos de Godot" #: editor/editor_node.cpp msgid "Support Godot Development" @@ -3262,10 +3286,6 @@ msgid "Save & Restart" msgstr "Enregistrer et redémarrer" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "Tourne lorsque la fenêtre de l'éditeur est redessinée." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "Mettre à jour en continu" @@ -3308,14 +3328,12 @@ msgid "Manage Templates" msgstr "Gérer les modèles" #: editor/editor_node.cpp -#, fuzzy msgid "Install from file" -msgstr "Installer depuis fichier" +msgstr "Installer depuis un fichier" #: editor/editor_node.cpp -#, fuzzy msgid "Select android sources file" -msgstr "Sélectionnez un maillage source :" +msgstr "Sélectionner un fichier de sources Android" #: editor/editor_node.cpp msgid "" @@ -3398,9 +3416,8 @@ msgid "Select" msgstr "Sélectionner" #: editor/editor_node.cpp -#, fuzzy msgid "Select Current" -msgstr "Sélectionner le dossier courant" +msgstr "Sélectionner le dossier actuel" #: editor/editor_node.cpp msgid "Open 2D Editor" @@ -3435,9 +3452,8 @@ msgid "No sub-resources found." msgstr "Aucune sous-ressource n'a été trouvée." #: editor/editor_path.cpp -#, fuzzy msgid "Open a list of sub-resources." -msgstr "Aucune sous-ressource n'a été trouvée." +msgstr "Ouvrir une liste de sous-ressources." #: editor/editor_plugin.cpp msgid "Creating Mesh Previews" @@ -3464,14 +3480,12 @@ msgid "Update" msgstr "Mettre à jour" #: editor/editor_plugin_settings.cpp -#, fuzzy msgid "Version" -msgstr "Version :" +msgstr "Version" #: editor/editor_plugin_settings.cpp -#, fuzzy msgid "Author" -msgstr "Auteurs" +msgstr "Auteur" #: editor/editor_plugin_settings.cpp #: editor/plugins/version_control_editor_plugin.cpp @@ -3481,25 +3495,23 @@ msgstr "État" #: editor/editor_profiler.cpp msgid "Measure:" -msgstr "Mesure :" +msgstr "Mesurer :" #: editor/editor_profiler.cpp -#, fuzzy msgid "Frame Time (ms)" -msgstr "Temps par trame (seconde)" +msgstr "Temps par image (ms)" #: editor/editor_profiler.cpp -#, fuzzy msgid "Average Time (ms)" -msgstr "Temps moyen (seconde)" +msgstr "Temps moyen (ms)" #: editor/editor_profiler.cpp msgid "Frame %" -msgstr "Trame %" +msgstr "Image %" #: editor/editor_profiler.cpp msgid "Physics Frame %" -msgstr "Trame physique %" +msgstr "Image physique %" #: editor/editor_profiler.cpp msgid "Inclusive" @@ -3518,10 +3530,17 @@ msgid "" "functions called by that function.\n" "Use this to find individual functions to optimize." msgstr "" +"Inclusif : Inclut le temps passé dans les fonctions appelées par cette " +"fonction.\n" +"Utilisez ce mode pour repérer les goulots d'étranglement.\n" +"\n" +"Self : N'inclure que le temps passé dans la fonction elle-même, et non le " +"temps passé dans d'autres fonctions appelées par cette fonction.\n" +"Utilisez ce mode pour trouver des fonctions individuelles à optimiser." #: editor/editor_profiler.cpp msgid "Frame #:" -msgstr "Trame # :" +msgstr "Image # :" #: editor/editor_profiler.cpp msgid "Time" @@ -3640,9 +3659,8 @@ msgid "Paste" msgstr "Coller" #: editor/editor_resource_picker.cpp editor/property_editor.cpp -#, fuzzy msgid "Convert to %s" -msgstr "Convertir en %s" +msgstr "Convertir vers %s" #: editor/editor_resource_picker.cpp editor/property_editor.cpp msgid "New %s" @@ -3690,11 +3708,10 @@ msgid "Did you forget the '_run' method?" msgstr "Avez-vous oublié la méthode « _run » ?" #: editor/editor_spin_slider.cpp -#, fuzzy msgid "Hold %s to round to integers. Hold Shift for more precise changes." msgstr "" -"Maintenir Ctrl pour arrondir à l'entier. Maintenir Maj pour des changements " -"plus précis." +"Maintenir %s pour arrondir à l'entier près. Maintenir Maj. pour des " +"changements plus précis." #: editor/editor_sub_scene.cpp msgid "Select Node(s) to Import" @@ -3714,49 +3731,43 @@ msgstr "Importer à partir d'un nÅ“ud :" #: editor/export_template_manager.cpp msgid "Open the folder containing these templates." -msgstr "" +msgstr "Ouvrir le dossier contenant ces modèles." #: editor/export_template_manager.cpp msgid "Uninstall these templates." -msgstr "" +msgstr "Désinstaller ces modèles." #: editor/export_template_manager.cpp -#, fuzzy msgid "There are no mirrors available." -msgstr "'%s' n'existe pas." +msgstr "Il n'y a pas de miroirs disponibles." #: editor/export_template_manager.cpp -#, fuzzy msgid "Retrieving the mirror list..." -msgstr "Récupération des miroirs, veuillez patienter..." +msgstr "Récupération des miroirs…" #: editor/export_template_manager.cpp msgid "Starting the download..." -msgstr "" +msgstr "Démarrage du téléchargement…" #: editor/export_template_manager.cpp msgid "Error requesting URL:" msgstr "Erreur lors de la demande de l’URL :" #: editor/export_template_manager.cpp -#, fuzzy msgid "Connecting to the mirror..." -msgstr "Connexion au Miroir..." +msgstr "Connexion au miroir…" #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't resolve the requested address." -msgstr "Impossible de résoudre le nom de l'hôte :" +msgstr "Impossible de résoudre l'adresse demandée." #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't connect to the mirror." -msgstr "Connexion à l'hôte impossible :" +msgstr "Impossible de se connecter au miroir." #: editor/export_template_manager.cpp -#, fuzzy msgid "No response from the mirror." -msgstr "Pas de réponse de l'hôte :" +msgstr "Pas de réponse du miroir." #: editor/export_template_manager.cpp #: editor/plugins/asset_library_editor_plugin.cpp @@ -3764,22 +3775,20 @@ msgid "Request failed." msgstr "La requête a échoué." #: editor/export_template_manager.cpp -#, fuzzy msgid "Request ended up in a redirect loop." -msgstr "La requête a échoué, trop de redirections" +msgstr "La requête a échoué car il y a eu trop de redirections." #: editor/export_template_manager.cpp -#, fuzzy msgid "Request failed:" -msgstr "La requête a échoué." +msgstr "La requête a échoué :" #: editor/export_template_manager.cpp msgid "Download complete; extracting templates..." -msgstr "" +msgstr "Téléchargement terminé, extraction des modèles…" #: editor/export_template_manager.cpp msgid "Cannot remove temporary file:" -msgstr "Impossible de supprimer le fichier temporaire :" +msgstr "Impossible de supprimer le fichier temporaire :" #: editor/export_template_manager.cpp msgid "" @@ -3787,14 +3796,13 @@ msgid "" "The problematic templates archives can be found at '%s'." msgstr "" "L'installation des modèles a échoué.\n" -"Les archives des modèles problématiques se trouvent dans '%s'." +"Les archives des modèles problématiques se trouvent dans « %s »." #: editor/export_template_manager.cpp msgid "Error getting the list of mirrors." msgstr "Erreur lors du téléchargement de la liste des miroirs." #: editor/export_template_manager.cpp -#, fuzzy msgid "Error parsing JSON with the list of mirrors. Please report this issue!" msgstr "" "Erreur lors de la lecture de la liste JSON des miroirs. Merci de signaler ce " @@ -3802,7 +3810,7 @@ msgstr "" #: editor/export_template_manager.cpp msgid "Best available mirror" -msgstr "" +msgstr "Meilleur miroir disponible" #: editor/export_template_manager.cpp msgid "" @@ -3855,25 +3863,20 @@ msgid "SSL Handshake Error" msgstr "Erreurs de la négociation SSL" #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't open the export templates file." -msgstr "" -"Impossible d'ouvrir le fichier ZIP contenant les modèles d'exportation." +msgstr "Impossible d'ouvrir le fichier contenant les modèles d'exportation." #: editor/export_template_manager.cpp -#, fuzzy msgid "Invalid version.txt format inside the export templates file: %s." -msgstr "Format de version.txt invalide dans les modèles : %s." +msgstr "Format de version.txt invalide dans le fichier de modèles : %s." #: editor/export_template_manager.cpp -#, fuzzy msgid "No version.txt found inside the export templates file." -msgstr "Aucun version.txt n'a été trouvé dans les modèles." +msgstr "Aucun fichier version.txt n'a été trouvé dans le fichier des modèles." #: editor/export_template_manager.cpp -#, fuzzy msgid "Error creating path for extracting templates:" -msgstr "Erreur lors de la création du chemin pour les modèles :" +msgstr "Erreur lors de la création du chemin pour l'extraction des modèles :" #: editor/export_template_manager.cpp msgid "Extracting Export Templates" @@ -3884,9 +3887,8 @@ msgid "Importing:" msgstr "Importation :" #: editor/export_template_manager.cpp -#, fuzzy msgid "Remove templates for the version '%s'?" -msgstr "Supprimer la version « %s » du modèle ?" +msgstr "Supprimer les modèles pour la version « %s » ?" #: editor/export_template_manager.cpp msgid "Uncompressing Android Build Sources" @@ -3903,43 +3905,56 @@ msgstr "Version courante :" #: editor/export_template_manager.cpp msgid "Export templates are missing. Download them or install from a file." msgstr "" +"Les modèles d'exportation sont manquants. Téléchargez-les ou installez-les " +"depuis un fichier." #: editor/export_template_manager.cpp msgid "Export templates are installed and ready to be used." -msgstr "" +msgstr "Les modèles d'exportation sont installés et prêts à être utilisés." #: editor/export_template_manager.cpp -#, fuzzy msgid "Open Folder" -msgstr "Ouvrir le fichier" +msgstr "Ouvrir le dossier" #: editor/export_template_manager.cpp msgid "Open the folder containing installed templates for the current version." msgstr "" +"Ouvrir le dossier contenant les modèles d'exportation pour la version " +"actuelle." #: editor/export_template_manager.cpp msgid "Uninstall" msgstr "Désinstaller" #: editor/export_template_manager.cpp -#, fuzzy msgid "Uninstall templates for the current version." -msgstr "Valeur initiale pour le compteur" +msgstr "Désinstaller les modèles d'exportation pour la version actuelle." #: editor/export_template_manager.cpp -#, fuzzy msgid "Download from:" -msgstr "Erreur de téléchargement" +msgstr "Télécharger depuis :" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Exécuter dans le navigateur" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "Copier l'erreur" #: editor/export_template_manager.cpp msgid "Download and Install" -msgstr "" +msgstr "Télécharger et installer" #: editor/export_template_manager.cpp msgid "" "Download and install templates for the current version from the best " "possible mirror." msgstr "" +"Télécharger et installer les modèles d'exportation pour la version actuelle " +"depuis le meilleur miroir disponible." #: editor/export_template_manager.cpp msgid "Official export templates aren't available for development builds." @@ -3948,14 +3963,12 @@ msgstr "" "versions de développement." #: editor/export_template_manager.cpp -#, fuzzy msgid "Install from File" -msgstr "Installer depuis fichier" +msgstr "Installer depuis un fichier" #: editor/export_template_manager.cpp -#, fuzzy msgid "Install templates from a local file." -msgstr "Importer des modèles depuis un fichier ZIP" +msgstr "Installer des modèles d'exportation depuis un fichier local." #: editor/export_template_manager.cpp editor/find_in_files.cpp #: editor/progress_dialog.cpp scene/gui/dialogs.cpp @@ -3963,24 +3976,20 @@ msgid "Cancel" msgstr "Annuler" #: editor/export_template_manager.cpp -#, fuzzy msgid "Cancel the download of the templates." -msgstr "" -"Impossible d'ouvrir le fichier ZIP contenant les modèles d'exportation." +msgstr "Annuler le téléchargement des modèles d'exportation." #: editor/export_template_manager.cpp -#, fuzzy msgid "Other Installed Versions:" -msgstr "Versions installées :" +msgstr "Autres versions installées :" #: editor/export_template_manager.cpp -#, fuzzy msgid "Uninstall Template" -msgstr "Désinstaller" +msgstr "Désinstaller le modèle" #: editor/export_template_manager.cpp msgid "Select Template File" -msgstr "Sélectionner le fichier de modèle" +msgstr "Sélectionner le fichier de modèles" #: editor/export_template_manager.cpp msgid "Godot Export Templates" @@ -3991,6 +4000,10 @@ msgid "" "The templates will continue to download.\n" "You may experience a short editor freeze when they finish." msgstr "" +"Les modèles d'exportation vont continuer à être téléchargés en arrière-" +"plan.\n" +"Vous pourrez peut-être remarquer un court gel de l'éditeur lorsque le " +"téléchargement est terminé." #: editor/filesystem_dock.cpp msgid "Favorites" @@ -4138,35 +4151,32 @@ msgid "Collapse All" msgstr "Réduire tout" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort files" -msgstr "Rechercher des fichiers" +msgstr "Trier les fichiers" #: editor/filesystem_dock.cpp msgid "Sort by Name (Ascending)" -msgstr "" +msgstr "Trier par nom (ascendant)" #: editor/filesystem_dock.cpp msgid "Sort by Name (Descending)" -msgstr "" +msgstr "Trier par nom (descendant)" #: editor/filesystem_dock.cpp msgid "Sort by Type (Ascending)" -msgstr "" +msgstr "Trier par type (ascendant)" #: editor/filesystem_dock.cpp msgid "Sort by Type (Descending)" -msgstr "" +msgstr "Trier par type (descendant)" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort by Last Modified" -msgstr "Dernière modification" +msgstr "Trier par date de modification (plus récent au moins récent)" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort by First Modified" -msgstr "Dernière modification" +msgstr "Trier par date de modification (moins récent au plus récent)" #: editor/filesystem_dock.cpp msgid "Duplicate..." @@ -4178,7 +4188,7 @@ msgstr "Renommer..." #: editor/filesystem_dock.cpp msgid "Focus the search box" -msgstr "" +msgstr "Mettre la boîte de recherche en surbrillance" #: editor/filesystem_dock.cpp msgid "Previous Folder/File" @@ -4488,14 +4498,12 @@ msgid "Failed to load resource." msgstr "Impossible de charger la ressource." #: editor/inspector_dock.cpp -#, fuzzy msgid "Copy Properties" -msgstr "Propriétés" +msgstr "Copier les propriétés" #: editor/inspector_dock.cpp -#, fuzzy msgid "Paste Properties" -msgstr "Propriétés" +msgstr "Coller les propriétés" #: editor/inspector_dock.cpp msgid "Make Sub-Resources Unique" @@ -4520,23 +4528,20 @@ msgid "Save As..." msgstr "Enregistrer sous…" #: editor/inspector_dock.cpp -#, fuzzy msgid "Extra resource options." -msgstr "Pas dans le chemin de la ressource." +msgstr "Options de ressource additionnelles." #: editor/inspector_dock.cpp -#, fuzzy msgid "Edit Resource from Clipboard" -msgstr "Modifier le Presse-papiers de la ressource" +msgstr "Modifier la ressource depuis le presse-papiers" #: editor/inspector_dock.cpp msgid "Copy Resource" msgstr "Copier la ressource" #: editor/inspector_dock.cpp -#, fuzzy msgid "Make Resource Built-In" -msgstr "Rendre intégré" +msgstr "Rendre la ressource intégrée" #: editor/inspector_dock.cpp msgid "Go to the previous edited object in history." @@ -4551,9 +4556,8 @@ msgid "History of recently edited objects." msgstr "Historique des objets récemment édités." #: editor/inspector_dock.cpp -#, fuzzy msgid "Open documentation for this object." -msgstr "Ouvrir la documentation" +msgstr "Ouvrir la documentation pour cet objet." #: editor/inspector_dock.cpp editor/scene_tree_dock.cpp msgid "Open Documentation" @@ -4564,9 +4568,8 @@ msgid "Filter properties" msgstr "Filtrer les propriétés" #: editor/inspector_dock.cpp -#, fuzzy msgid "Manage object properties." -msgstr "Propriétés de l'objet." +msgstr "Gérer les propriétés de l'objet." #: editor/inspector_dock.cpp msgid "Changes may be lost!" @@ -4814,9 +4817,8 @@ msgid "Blend:" msgstr "Mélange :" #: editor/plugins/animation_blend_tree_editor_plugin.cpp -#, fuzzy msgid "Parameter Changed:" -msgstr "Paramètre modifié" +msgstr "Paramètre modifié :" #: editor/plugins/animation_blend_tree_editor_plugin.cpp #: editor/plugins/animation_tree_player_editor_plugin.cpp @@ -5550,11 +5552,11 @@ msgstr "All" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Search templates, projects, and demos" -msgstr "" +msgstr "Rechercher modèles, projets et démos" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Search assets (excluding templates, projects, and demos)" -msgstr "" +msgstr "Rechercher des assets (à l'exception des modèles, projets et démos)" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Import..." @@ -5598,7 +5600,7 @@ msgstr "Fichier ZIP de données" #: editor/plugins/audio_stream_editor_plugin.cpp msgid "Audio Preview Play/Pause" -msgstr "" +msgstr "Aperçu audio lecture/pause" #: editor/plugins/baked_lightmap_editor_plugin.cpp msgid "" @@ -5859,13 +5861,12 @@ msgstr "Modifier les ancres" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "" "Project Camera Override\n" "Overrides the running project's camera with the editor viewport camera." msgstr "" -"Remplacement de la Caméra du Jeu\n" -"Remplace la caméra du jeu par la caméra de la fenêtre d'affichage de " +"Remplacement de la caméra du projet\n" +"Remplace la caméra du projet par la caméra de la fenêtre d'affichage de " "l'editeur." #: editor/plugins/canvas_item_editor_plugin.cpp @@ -5875,6 +5876,9 @@ msgid "" "No project instance running. Run the project from the editor to use this " "feature." msgstr "" +"Remplacement de la caméra du projet\n" +"Pas d'instance du projet en cours d'exécution. Lancez le projet depuis " +"l'éditeur afin d'utiliser cette fonctionnalité." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp @@ -5942,31 +5946,27 @@ msgstr "Mode sélection" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Drag: Rotate selected node around pivot." -msgstr "Supprimer le nÅ“ud sélectionné ou la transition." +msgstr "Glisser : Tourner le nÅ“ud sélectionné autour du pivot." #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Alt+Drag: Move selected node." -msgstr "Alt+Glisser : déplacer" +msgstr "Alt + Glisser : Déplacer le nÅ“ud sélectionné." #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "V: Set selected node's pivot position." -msgstr "Supprimer le nÅ“ud sélectionné ou la transition." +msgstr "V : Définir la position du pivot pour le nÅ“ud sélectionné." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Alt+RMB: Show list of all nodes at position clicked, including locked." msgstr "" -"Afficher une liste de tous les objets à la position cliquée\n" -"(identique à Alt+Bouton droit en mode sélection)." +"Alt + Clic droit : Afficher une liste de tous les nÅ“uds à la position " +"cliquée, y compris les nÅ“uds verrouillés." #: editor/plugins/canvas_item_editor_plugin.cpp msgid "RMB: Add node at position clicked." -msgstr "" +msgstr "Clic droit : Ajouter un nÅ“ud à la position cliquée." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp @@ -6204,14 +6204,12 @@ msgid "Clear Pose" msgstr "Vider la pose" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Add Node Here" -msgstr "Ajouter un nÅ“ud" +msgstr "Ajouter un nÅ“ud ici" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Instance Scene Here" -msgstr "Instancier scène(s)" +msgstr "Instancer une scène ici" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Multiply grid step by 2" @@ -6227,49 +6225,43 @@ msgstr "Vue panoramique" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 3.125%" -msgstr "" +msgstr "Zoomer à 3.125%" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 6.25%" -msgstr "" +msgstr "Zoomer à 6.25%" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 12.5%" -msgstr "" +msgstr "Zoomer à 12.5%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 25%" -msgstr "Dézoomer" +msgstr "Zoomer à 25%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 50%" -msgstr "Dézoomer" +msgstr "Zoomer à 50%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 100%" -msgstr "Dézoomer" +msgstr "Zoomer à 100%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 200%" -msgstr "Dézoomer" +msgstr "Zoomer à 200%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 400%" -msgstr "Dézoomer" +msgstr "Zoomer à 400%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 800%" -msgstr "Dézoomer" +msgstr "Zoomer à 800%" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 1600%" -msgstr "" +msgstr "Zoomer à 1600%" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Add %s" @@ -6516,9 +6508,8 @@ msgid "Couldn't create a single convex collision shape." msgstr "Impossible de créer une forme de collision convexe unique." #: editor/plugins/mesh_instance_editor_plugin.cpp -#, fuzzy msgid "Create Simplified Convex Shape" -msgstr "Créer une forme convexe unique" +msgstr "Créer une forme convexe simplifiée" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Create Single Convex Shape" @@ -6557,9 +6548,8 @@ msgid "No mesh to debug." msgstr "Aucun maillage à déboguer." #: editor/plugins/mesh_instance_editor_plugin.cpp -#, fuzzy msgid "Mesh has no UV in layer %d." -msgstr "Le modèle n'a pas d'UV dans cette couche" +msgstr "Le maillage n'a pas d'UV dans la couche %d." #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "MeshInstance lacks a Mesh!" @@ -6628,9 +6618,8 @@ msgstr "" "collisions." #: editor/plugins/mesh_instance_editor_plugin.cpp -#, fuzzy msgid "Create Simplified Convex Collision Sibling" -msgstr "Créer une seule collision convexe sÅ“ur" +msgstr "Créer une collision sÅ“ur convexe simplifiée" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "" @@ -6638,20 +6627,23 @@ msgid "" "This is similar to single collision shape, but can result in a simpler " "geometry in some cases, at the cost of accuracy." msgstr "" +"Créé une forme de collision complexe simplifiée.\n" +"Cela est similaire à une forme de collision, mais peut résulter en une " +"géométrie plus simple mais moins précise." #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Create Multiple Convex Collision Siblings" msgstr "Créer plusieurs collisions convexes sÅ“urs" #: editor/plugins/mesh_instance_editor_plugin.cpp -#, fuzzy msgid "" "Creates a polygon-based collision shape.\n" "This is a performance middle-ground between a single convex collision and a " "polygon-based collision." msgstr "" -"Crée une forme de collision basée sur les polygones.\n" -"Il s'agit d'une performance à mi-chemin entre les deux options ci-dessus." +"Crée une forme de collision basée sur des polygones.\n" +"Il s'agit d'une performance à mi-chemin entre une forme unique de collision " +"convexe et une collision basée sur des polygones." #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Create Outline Mesh..." @@ -7301,24 +7293,20 @@ msgid "ResourcePreloader" msgstr "ResourcePreloader" #: editor/plugins/room_manager_editor_plugin.cpp -#, fuzzy msgid "Flip Portals" -msgstr "Retourner horizontalement" +msgstr "Retourner les Portals" #: editor/plugins/room_manager_editor_plugin.cpp -#, fuzzy msgid "Room Generate Points" -msgstr "Compte de Points Générés :" +msgstr "Générer des points Room" #: editor/plugins/room_manager_editor_plugin.cpp -#, fuzzy msgid "Generate Points" -msgstr "Compte de Points Générés :" +msgstr "Générer des points" #: editor/plugins/room_manager_editor_plugin.cpp -#, fuzzy msgid "Flip Portal" -msgstr "Retourner horizontalement" +msgstr "Retourner le Portal" #: editor/plugins/root_motion_editor_plugin.cpp msgid "AnimationTree has no path set to an AnimationPlayer" @@ -7882,20 +7870,17 @@ msgid "None" msgstr "Aucun" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Rotate" -msgstr "Mode rotation" +msgstr "Rotation" #. TRANSLATORS: This refers to the movement that changes the position of an object. #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Translate" -msgstr "Translater :" +msgstr "Translation" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Scale" -msgstr "Échelle :" +msgstr "Mode mise à l'échelle" #: editor/plugins/spatial_editor_plugin.cpp msgid "Scaling: " @@ -7918,52 +7903,44 @@ msgid "Animation Key Inserted." msgstr "Clé d'animation insérée." #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Pitch:" -msgstr "Tangage (latéral)" +msgstr "Tangage :" #: editor/plugins/spatial_editor_plugin.cpp msgid "Yaw:" -msgstr "" +msgstr "Azimuth :" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Size:" -msgstr "Taille : " +msgstr "Taille :" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Objects Drawn:" -msgstr "Objets dessinés" +msgstr "Objets dessinés :" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Material Changes:" -msgstr "Modifications de materiau" +msgstr "Changements de matériau :" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Shader Changes:" -msgstr "Modification de shader" +msgstr "Changements de shader :" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Surface Changes:" -msgstr "Modifications de surface" +msgstr "Changements de surface :" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Draw Calls:" -msgstr "Appels de graphes" +msgstr "Appels de dessin :" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Vertices:" -msgstr "Vertex" +msgstr "Sommets :" #: editor/plugins/spatial_editor_plugin.cpp msgid "FPS: %d (%s ms)" -msgstr "" +msgstr "FPS : %d (%s ms)" #: editor/plugins/spatial_editor_plugin.cpp msgid "Top View." @@ -8120,9 +8097,8 @@ msgid "Freelook Slow Modifier" msgstr "Ralentissement de la vue libre" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Toggle Camera Preview" -msgstr "Changer la taille d'une caméra" +msgstr "Activer la prévisualisation de la caméra" #: editor/plugins/spatial_editor_plugin.cpp msgid "View Rotation Locked" @@ -8145,9 +8121,8 @@ msgstr "" "performance en jeu." #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Convert Rooms" -msgstr "Convertir en %s" +msgstr "Convertir les salles" #: editor/plugins/spatial_editor_plugin.cpp msgid "XForm Dialog" @@ -8169,7 +8144,6 @@ msgstr "" "(« rayon x »)." #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Snap Nodes to Floor" msgstr "Aligner les nÅ“uds avec le sol" @@ -8187,7 +8161,7 @@ msgstr "Utiliser l’aimantation" #: editor/plugins/spatial_editor_plugin.cpp msgid "Converts rooms for portal culling." -msgstr "" +msgstr "Convertit des salles pour l'occlusion culling à l'aide de portails." #: editor/plugins/spatial_editor_plugin.cpp msgid "Bottom View" @@ -8283,9 +8257,8 @@ msgid "View Grid" msgstr "Afficher la grille" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "View Portal Culling" -msgstr "Paramètres de la vue" +msgstr "Afficher le Portal culling" #: editor/plugins/spatial_editor_plugin.cpp #: modules/gridmap/grid_map_editor_plugin.cpp @@ -8599,230 +8572,208 @@ msgid "Step:" msgstr "Pas (s) :" #: editor/plugins/texture_region_editor_plugin.cpp -#, fuzzy msgid "Separation:" -msgstr "Recensements :" +msgstr "Séparation :" #: editor/plugins/texture_region_editor_plugin.cpp msgid "TextureRegion" msgstr "RegionDeTexture" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Colors" -msgstr "Couleur" +msgstr "Couleurs" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Fonts" -msgstr "Police" +msgstr "Polices" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Icons" -msgstr "Icône" +msgstr "Icônes" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Styleboxes" -msgstr "Style" +msgstr "Styleboxes" #: editor/plugins/theme_editor_plugin.cpp msgid "{num} color(s)" -msgstr "" +msgstr "{num} couleur(s)" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No colors found." -msgstr "Aucune sous-ressource n'a été trouvée." +msgstr "Pas de couleurs trouvées." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "{num} constant(s)" -msgstr "Constantes" +msgstr "{num} constante(s)" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No constants found." -msgstr "Constante de couleur." +msgstr "Pas de constantes trouvées." #: editor/plugins/theme_editor_plugin.cpp msgid "{num} font(s)" -msgstr "" +msgstr "{num} police(s)" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No fonts found." -msgstr "Aucune sous-ressource n'a été trouvée." +msgstr "Pas de polices trouvées." #: editor/plugins/theme_editor_plugin.cpp msgid "{num} icon(s)" -msgstr "" +msgstr "{num} icône(s)" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No icons found." -msgstr "Aucune sous-ressource n'a été trouvée." +msgstr "Pas d'icônes trouvées." #: editor/plugins/theme_editor_plugin.cpp msgid "{num} stylebox(es)" -msgstr "" +msgstr "{num} stylebox(es)" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No styleboxes found." -msgstr "Aucune sous-ressource n'a été trouvée." +msgstr "Pas de styleboxes trouvées." #: editor/plugins/theme_editor_plugin.cpp msgid "{num} currently selected" -msgstr "" +msgstr "{num} actuellement sélectionné(s)" #: editor/plugins/theme_editor_plugin.cpp msgid "Nothing was selected for the import." -msgstr "" +msgstr "Rien n'a été sélectionné pour l'importation." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Importing Theme Items" -msgstr "Importer un thème" +msgstr "Importation des items de thème" #: editor/plugins/theme_editor_plugin.cpp msgid "Importing items {n}/{n}" -msgstr "" +msgstr "Importation de l'item {n}/{n}" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Updating the editor" -msgstr "Quitter l'éditeur ?" +msgstr "Mise à jour de l'éditeur" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Finalizing" -msgstr "Analyse" +msgstr "Finalisation" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Filter:" -msgstr "Filtres :" +msgstr "Filtre :" #: editor/plugins/theme_editor_plugin.cpp msgid "With Data" -msgstr "" +msgstr "Avec données" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select by data type:" -msgstr "Sélectionner un nÅ“ud" +msgstr "Sélectionner par type de données :" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible color items." -msgstr "Sélectionnez un fractionnement à effacer." +msgstr "Sélectionner tous les items de couleur visibles." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible color items and their data." msgstr "" +"Sélectionner tous les items de couleur visibles ainsi que leurs données." #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all visible color items." -msgstr "" +msgstr "Désélectionner tous les items de couleur visibles." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible constant items." -msgstr "Sélectionnez d'abord un élément à configurer !" +msgstr "Sélectionner tous les items de constantes visibles." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible constant items and their data." msgstr "" +"Sélectionner tous les items de constantes visibles ainsi que leurs données." #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all visible constant items." -msgstr "" +msgstr "Désélectionner tous les items de constantes visibles." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible font items." -msgstr "Sélectionnez d'abord un élément à configurer !" +msgstr "Sélectionner tous les items de police visibles." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible font items and their data." msgstr "" +"Sélectionner tous les items de police visibles ainsi que leurs données." #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all visible font items." -msgstr "" +msgstr "Désélectionner tous les items de police visibles." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible icon items." -msgstr "Sélectionnez d'abord un élément à configurer !" +msgstr "Sélectionner tous les items d'icône visibles." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible icon items and their data." -msgstr "Sélectionnez d'abord un élément à configurer !" +msgstr "Sélectionner tous les items d'icône visibles ainsi que leurs données." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Deselect all visible icon items." -msgstr "Sélectionnez d'abord un élément à configurer !" +msgstr "Désélectionner tous les items d'icône visibles." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible stylebox items." -msgstr "" +msgstr "Sélectionner tous les items de stylebox visibles." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible stylebox items and their data." msgstr "" +"Sélectionner tous les items de stylebox visibles ainsi que leurs données." #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all visible stylebox items." -msgstr "" +msgstr "Désélectionner tous les items de stylebox visibles." #: editor/plugins/theme_editor_plugin.cpp msgid "" "Caution: Adding icon data may considerably increase the size of your Theme " "resource." msgstr "" +"Attention : Ajouter des données d'icônes peut augmenter considérablement la " +"taille de votre ressource Theme." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Collapse types." -msgstr "Réduire tout" +msgstr "Réduire les types." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Expand types." -msgstr "Développer tout" +msgstr "Développer les types." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all Theme items." -msgstr "Sélectionner le fichier de modèle" +msgstr "Sélectionner tous les items du Theme." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select With Data" -msgstr "Sélectionner des points" +msgstr "Sélectionner avec les données" #: editor/plugins/theme_editor_plugin.cpp msgid "Select all Theme items with item data." -msgstr "" +msgstr "Sélectionne tous les items de thème avec les données d'item." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Deselect All" -msgstr "Tout sélectionner" +msgstr "Tout déselectionner" #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all Theme items." -msgstr "" +msgstr "Déselectionne tous les items du Theme." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Import Selected" -msgstr "Importer une scène" +msgstr "Importer la sélection" #: editor/plugins/theme_editor_plugin.cpp msgid "" @@ -8830,275 +8781,253 @@ msgid "" "closing this window.\n" "Close anyway?" msgstr "" +"L'onglet Importer des items a des items sélectionnés. La sélection sera " +"perdue si vous fermez cette fenêtre.\n" +"Fermer tout de même ?" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All Color Items" -msgstr "Supprimer tous" +msgstr "Supprimer tous les items de couleur" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Item" -msgstr "Supprimer l'item" +msgstr "Renommer l'item" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All Constant Items" -msgstr "Supprimer tous" +msgstr "Supprimer tous les items de constante" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All Font Items" -msgstr "Supprimer tous" +msgstr "Supprimer tous les items de police" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All Icon Items" -msgstr "Supprimer tous" +msgstr "Supprimer tous les items d'icône" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All StyleBox Items" -msgstr "Supprimer tous" +msgstr "Supprimer tous les items de stylebox" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Color Item" -msgstr "Ajouter des items de classe" +msgstr "Ajouter un item de couleur" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Constant Item" -msgstr "Ajouter des items de classe" +msgstr "Ajouter un item de constante" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Font Item" -msgstr "Ajouter un item" +msgstr "Ajouter un item de police" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Icon Item" -msgstr "Ajouter un item" +msgstr "Ajouter un item d'icône" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Stylebox Item" -msgstr "Ajouter tous les items" +msgstr "Ajouter un item de stylebox" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Color Item" -msgstr "Supprimer des items de classe" +msgstr "Renommer l'item de couleur" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Constant Item" -msgstr "Supprimer des items de classe" +msgstr "Renommer l'item de constante" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Font Item" -msgstr "Renommer le nÅ“ud" +msgstr "Renommer l'item de police" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Icon Item" -msgstr "Renommer le nÅ“ud" +msgstr "Renommer l'item d'icône" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Stylebox Item" -msgstr "Supprimer l'élément sélectionné" +msgstr "Renommer l'item de stylebox" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Invalid file, not a Theme resource." -msgstr "Fichier invalide, pas une disposition de bus audio." +msgstr "Fichier invalide, car ce n'est pas une ressource de type Theme." #: editor/plugins/theme_editor_plugin.cpp msgid "Invalid file, same as the edited Theme resource." msgstr "" +"Fichier invalide, car il est identique à la ressource Theme actuellement en " +"cours de modification." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Manage Theme Items" -msgstr "Gérer les modèles" +msgstr "Gérer les items de thème" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Edit Items" -msgstr "Élément modifiable" +msgstr "Modifier les items" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Types:" -msgstr "Type :" +msgstr "Types :" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Type:" -msgstr "Type :" +msgstr "Ajouter un type :" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Item:" -msgstr "Ajouter un item" +msgstr "Ajouter un item :" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add StyleBox Item" -msgstr "Ajouter tous les items" +msgstr "Ajouter un item de stylebox" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove Items:" -msgstr "Supprimer l'item" +msgstr "Supprimer items :" #: editor/plugins/theme_editor_plugin.cpp msgid "Remove Class Items" -msgstr "Supprimer des items de classe" +msgstr "Supprimer les items de classe" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove Custom Items" -msgstr "Supprimer des items de classe" +msgstr "Supprimer les items personnalisés" #: editor/plugins/theme_editor_plugin.cpp msgid "Remove All Items" -msgstr "Supprimer tous" +msgstr "Supprimer tous les items" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Theme Item" -msgstr "Ajouter un item" +msgstr "Ajouter un item de thème" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Old Name:" -msgstr "Nom de nÅ“ud :" +msgstr "Ancien nom :" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Import Items" -msgstr "Importer un thème" +msgstr "Importer des items" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Default Theme" -msgstr "Par défaut" +msgstr "Thème par défaut" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Editor Theme" -msgstr "Modifier le thème" +msgstr "Thème de l'éditeur" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select Another Theme Resource:" -msgstr "Supprimer une ressource" +msgstr "Sélectionnez une autre ressource Theme :" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Another Theme" -msgstr "Importer un thème" +msgstr "Autre thème" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Confirm Item Rename" -msgstr "Configurer la grille" +msgstr "Confirmer le renommage d'item" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Cancel Item Rename" -msgstr "Renommer par lot" +msgstr "Annuler le renommage d'item" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Override Item" -msgstr "Redéfinition" +msgstr "Remplacer l'item" #: editor/plugins/theme_editor_plugin.cpp msgid "Unpin this StyleBox as a main style." -msgstr "" +msgstr "Désépingler cette StyleBox comme style principal." #: editor/plugins/theme_editor_plugin.cpp msgid "" "Pin this StyleBox as a main style. Editing its properties will update the " "same properties in all other StyleBoxes of this type." msgstr "" +"Épingler cette StyleBox comme style principal. Modifier ses propriétés " +"mettra à jour les mêmes propriétés dans toutes les autres StyleBoxes " +"appartenant à ce type." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Type" -msgstr "Type" +msgstr "Ajouter un type" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Item Type" -msgstr "Ajouter un item" +msgstr "Ajouter un item de type" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Node Types:" -msgstr "Type de nÅ“ud" +msgstr "Types de nÅ“ud :" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Show Default" -msgstr "Charger défaut" +msgstr "Afficher par défaut" #: editor/plugins/theme_editor_plugin.cpp msgid "Show default type items alongside items that have been overridden." msgstr "" +"Afficher les items de type par défaut à côté de ceux qui ont été surchargés." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Override All" -msgstr "Redéfinition" +msgstr "Tout surcharger" #: editor/plugins/theme_editor_plugin.cpp msgid "Override all default type items." -msgstr "" +msgstr "Surcharge tous les items de type par défaut." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Theme:" -msgstr "Thème" +msgstr "Thème :" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Manage Items..." -msgstr "Gérer les modèles d'exportation..." +msgstr "Gérer les items…" #: editor/plugins/theme_editor_plugin.cpp msgid "Add, remove, organize and import Theme items." -msgstr "" +msgstr "Ajoute, supprime, organise et importe des items de thème." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Preview" -msgstr "Aperçu" +msgstr "Ajouter un aperçu" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Default Preview" -msgstr "Aperçu de la mise à jour" +msgstr "Aperçu par défaut" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select UI Scene:" -msgstr "Sélectionnez un maillage source :" +msgstr "Sélectionner une scène UI :" #: editor/plugins/theme_editor_preview.cpp msgid "" "Toggle the control picker, allowing to visually select control types for " "edit." msgstr "" +"Active le sélectionneur de contrôle, qui permet de sélectionner visuellement " +"des types de contrôles à modifier." #: editor/plugins/theme_editor_preview.cpp msgid "Toggle Button" -msgstr "Activer / Désactiver bouton" +msgstr "Bouton à bascule (toggle)" #: editor/plugins/theme_editor_preview.cpp msgid "Disabled Button" @@ -9110,7 +9039,7 @@ msgstr "Item" #: editor/plugins/theme_editor_preview.cpp msgid "Disabled Item" -msgstr "Élément Désactivé" +msgstr "Item désactivé" #: editor/plugins/theme_editor_preview.cpp msgid "Check Item" @@ -9129,9 +9058,8 @@ msgid "Checked Radio Item" msgstr "Item radio coché" #: editor/plugins/theme_editor_preview.cpp -#, fuzzy msgid "Named Separator" -msgstr "Séparateur nommé." +msgstr "Séparateur nommé" #: editor/plugins/theme_editor_preview.cpp msgid "Submenu" @@ -9184,19 +9112,22 @@ msgstr "Possède,Plusieurs,Options" #: editor/plugins/theme_editor_preview.cpp msgid "Invalid path, the PackedScene resource was probably moved or removed." msgstr "" +"Chemin invalide. La ressource PackedScene a probablement été déplacée ou " +"supprimée." #: editor/plugins/theme_editor_preview.cpp msgid "Invalid PackedScene resource, must have a Control node at its root." msgstr "" +"Ressource PackedScene invalide. La scène doit avoir un nÅ“ud de type Control " +"à sa racine." #: editor/plugins/theme_editor_preview.cpp -#, fuzzy msgid "Invalid file, not a PackedScene resource." -msgstr "Fichier invalide, pas une disposition de bus audio." +msgstr "Fichier invalide, pas une ressource PackedScene." #: editor/plugins/theme_editor_preview.cpp msgid "Reload the scene to reflect its most actual state." -msgstr "" +msgstr "Recharge la scène pour refléter son état le plus actuel." #: editor/plugins/tile_map_editor_plugin.cpp msgid "Erase Selection" @@ -10605,9 +10536,8 @@ msgid "VisualShader" msgstr "VisualShader" #: editor/plugins/visual_shader_editor_plugin.cpp -#, fuzzy msgid "Edit Visual Property:" -msgstr "Modifier la propriété visuelle" +msgstr "Modifier la propriété visuelle :" #: editor/plugins/visual_shader_editor_plugin.cpp msgid "Visual Shader Mode Changed" @@ -10733,9 +10663,8 @@ msgid "Script" msgstr "Script" #: editor/project_export.cpp -#, fuzzy msgid "GDScript Export Mode:" -msgstr "Mode d'exportation des scripts :" +msgstr "Mode d'exportation GDScript :" #: editor/project_export.cpp msgid "Text" @@ -10743,21 +10672,20 @@ msgstr "Texte" #: editor/project_export.cpp msgid "Compiled Bytecode (Faster Loading)" -msgstr "" +msgstr "Bytecode compilé (chargement plus rapide)" #: editor/project_export.cpp msgid "Encrypted (Provide Key Below)" msgstr "Chiffré (fournir clé ci-dessous)" #: editor/project_export.cpp -#, fuzzy msgid "Invalid Encryption Key (must be 64 hexadecimal characters long)" -msgstr "Clé de chiffrement invalide (doit comporter 64 caractères)" +msgstr "" +"Clé de chiffrement invalide (doit comporter 64 caractères hexadécimaux)" #: editor/project_export.cpp -#, fuzzy msgid "GDScript Encryption Key (256-bits as hexadecimal):" -msgstr "Clé de chiffrement des scripts (256 bits en hexadécimal) :" +msgstr "Clé de chiffrement GDScript (256 bits en hexadécimal) :" #: editor/project_export.cpp msgid "Export PCK/Zip" @@ -10832,7 +10760,6 @@ msgid "Imported Project" msgstr "Projet importé" #: editor/project_manager.cpp -#, fuzzy msgid "Invalid project name." msgstr "Nom du projet invalide." @@ -11061,14 +10988,12 @@ msgid "Are you sure to run %d projects at once?" msgstr "Voulez-vous vraiment lancer %d projets à la fois ?" #: editor/project_manager.cpp -#, fuzzy msgid "Remove %d projects from the list?" -msgstr "Sélectionner appareil depuis la liste" +msgstr "Retirer %d projets de la liste ?" #: editor/project_manager.cpp -#, fuzzy msgid "Remove this project from the list?" -msgstr "Sélectionner appareil depuis la liste" +msgstr "Retirer ce projet de la liste ?" #: editor/project_manager.cpp msgid "" @@ -11102,9 +11027,8 @@ msgid "Project Manager" msgstr "Gestionnaire de projets" #: editor/project_manager.cpp -#, fuzzy msgid "Local Projects" -msgstr "Projets" +msgstr "Projets locaux" #: editor/project_manager.cpp msgid "Loading, please wait..." @@ -11115,23 +11039,20 @@ msgid "Last Modified" msgstr "Dernière modification" #: editor/project_manager.cpp -#, fuzzy msgid "Edit Project" -msgstr "Exporter le projet" +msgstr "Modifier le projet" #: editor/project_manager.cpp -#, fuzzy msgid "Run Project" -msgstr "Renommer le projet" +msgstr "Lancer le projet" #: editor/project_manager.cpp msgid "Scan" msgstr "Scanner" #: editor/project_manager.cpp -#, fuzzy msgid "Scan Projects" -msgstr "Projets" +msgstr "Scanner des projets" #: editor/project_manager.cpp msgid "Select a Folder to Scan" @@ -11142,14 +11063,12 @@ msgid "New Project" msgstr "Nouveau projet" #: editor/project_manager.cpp -#, fuzzy msgid "Import Project" -msgstr "Projet importé" +msgstr "Importer un projet" #: editor/project_manager.cpp -#, fuzzy msgid "Remove Project" -msgstr "Renommer le projet" +msgstr "Retirer le projet" #: editor/project_manager.cpp msgid "Remove Missing" @@ -11160,9 +11079,8 @@ msgid "About" msgstr "À propos" #: editor/project_manager.cpp -#, fuzzy msgid "Asset Library Projects" -msgstr "Bibliothèque d'assets" +msgstr "Projets de l'Asset Library" #: editor/project_manager.cpp msgid "Restart Now" @@ -11174,7 +11092,7 @@ msgstr "Supprimer tout" #: editor/project_manager.cpp msgid "Also delete project contents (no undo!)" -msgstr "" +msgstr "Supprimer les contenus du projet également (pas d'annulation !)" #: editor/project_manager.cpp msgid "Can't run project" @@ -11189,12 +11107,10 @@ msgstr "" "Voulez-vous explorer des exemples de projets officiels dans l'Asset Library ?" #: editor/project_manager.cpp -#, fuzzy msgid "Filter projects" -msgstr "Filtrer les propriétés" +msgstr "Filtrer parmi les projets" #: editor/project_manager.cpp -#, fuzzy msgid "" "This field filters projects by name and last path component.\n" "To filter projects by name and full path, the query must contain at least " @@ -11211,7 +11127,7 @@ msgstr "Touche " #: editor/project_settings_editor.cpp msgid "Physical Key" -msgstr "" +msgstr "Touche physique" #: editor/project_settings_editor.cpp msgid "Joy Button" @@ -11259,7 +11175,7 @@ msgstr "Périphérique" #: editor/project_settings_editor.cpp msgid " (Physical)" -msgstr "" +msgstr " (physique)" #: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp msgid "Press a Key..." @@ -11402,23 +11318,23 @@ msgid "Override for Feature" msgstr "Écrasement d'un paramètre, dédié à un tag de fonctionnalité" #: editor/project_settings_editor.cpp -#, fuzzy msgid "Add %d Translations" -msgstr "Ajouter une traduction" +msgstr "Ajouter %d traductions" #: editor/project_settings_editor.cpp msgid "Remove Translation" msgstr "Supprimer la traduction" #: editor/project_settings_editor.cpp -#, fuzzy msgid "Translation Resource Remap: Add %d Path(s)" -msgstr "Réaffectation (remap) des ressources ; Ajouter une réaffectation" +msgstr "" +"Réaffectation (remap) des ressources par traduction : Ajouter %d chemin(s)" #: editor/project_settings_editor.cpp -#, fuzzy msgid "Translation Resource Remap: Add %d Remap(s)" -msgstr "Réaffectation (remap) des ressources ; Ajouter une réaffectation" +msgstr "" +"Réaffectation (remap) des ressources par traduction : Ajouter %d " +"réaffectation(s)" #: editor/project_settings_editor.cpp msgid "Change Resource Remap Language" @@ -11450,7 +11366,7 @@ msgstr "Général" #: editor/project_settings_editor.cpp msgid "Override For..." -msgstr "Surcharge pour…" +msgstr "Surcharger pour…" #: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp msgid "The editor must be restarted for changes to take effect." @@ -11862,12 +11778,16 @@ msgstr "Supprimer le noeud \"%s\" ?" msgid "" "Saving the branch as a scene requires having a scene open in the editor." msgstr "" +"Pour sauvegarder la branche en tant que scène, il faut qu'une scène soit " +"ouverte dans l'éditeur." #: editor/scene_tree_dock.cpp msgid "" "Saving the branch as a scene requires selecting only one node, but you have " "selected %d nodes." msgstr "" +"Pour sauvegarder la branche en tant que scène, il faut sélectionner " +"seulement un nÅ“ud, mais vous avez sélectionné %d nÅ“uds." #: editor/scene_tree_dock.cpp msgid "" @@ -11876,6 +11796,12 @@ msgid "" "FileSystem dock context menu\n" "or create an inherited scene using Scene > New Inherited Scene... instead." msgstr "" +"Impossible de sauvegarder la branche du nÅ“ud racine comme une scène " +"instanciée.\n" +"Pour créer une copie modifiable de la scène actuelle, dupliquez-la à l'aide " +"du menu contextuel du dock Système de fichiers\n" +"ou créez une scène héritée en utilisant Scène > Nouvelle scène héritée... à " +"la place." #: editor/scene_tree_dock.cpp msgid "" @@ -11883,6 +11809,10 @@ msgid "" "To create a variation of a scene, you can make an inherited scene based on " "the instanced scene using Scene > New Inherited Scene... instead." msgstr "" +"Impossible de sauvegarder la branche d'une scène déjà instanciée.\n" +"Pour créer une variation d'une scène, vous pouvez créer une scène héritée " +"basée sur la scène instanciée en utilisant Scène > Nouvelle scène héritée... " +"à la place." #: editor/scene_tree_dock.cpp msgid "Save New Scene As..." @@ -12291,6 +12221,8 @@ msgid "" "Warning: Having the script name be the same as a built-in type is usually " "not desired." msgstr "" +"Avertissement : Il n'est généralement pas souhaitable que le nom du script " +"soit le même que celui d'un type intégré." #: editor/script_create_dialog.cpp msgid "Class Name:" @@ -12362,7 +12294,7 @@ msgstr "Copier l'erreur" #: editor/script_editor_debugger.cpp msgid "Open C++ Source on GitHub" -msgstr "" +msgstr "Ouvrir les sources C++ sur GitHub" #: editor/script_editor_debugger.cpp msgid "Video RAM" @@ -12541,6 +12473,16 @@ msgstr "Changer la hauteur de la forme du cylindre" msgid "Change Ray Shape Length" msgstr "Changer la longueur d'une forme en rayon" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "Définir la position du point de la courbe" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "Définir la position du point de la courbe" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "Changer le rayon du cylindre" @@ -12656,12 +12598,11 @@ msgstr "L'objet ne peut fournir une longueur." #: modules/gltf/editor_scene_exporter_gltf_plugin.cpp #, fuzzy msgid "Export Mesh GLTF2" -msgstr "Exporter une bibliothèque de maillages" +msgstr "Exporter le Maillage GLTF2" #: modules/gltf/editor_scene_exporter_gltf_plugin.cpp -#, fuzzy msgid "Export GLTF..." -msgstr "Exporter..." +msgstr "Exporter en GLTF..." #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Next Plane" @@ -12704,9 +12645,8 @@ msgid "GridMap Paint" msgstr "Peinture GridMap" #: modules/gridmap/grid_map_editor_plugin.cpp -#, fuzzy msgid "GridMap Selection" -msgstr "Remplissage de la sélection de GridMap" +msgstr "Sélection de la GridMap" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Grid Map" @@ -12960,14 +12900,12 @@ msgid "Add Output Port" msgstr "Ajouter un port de sortie" #: modules/visual_script/visual_script_editor.cpp -#, fuzzy msgid "Change Port Type" -msgstr "Changer le type" +msgstr "Changer le Type de Port" #: modules/visual_script/visual_script_editor.cpp -#, fuzzy msgid "Change Port Name" -msgstr "Changer le nom du port d'entrée" +msgstr "Changer le Nom du Port" #: modules/visual_script/visual_script_editor.cpp msgid "Override an existing built-in function." @@ -13353,38 +13291,33 @@ msgid "Select device from the list" msgstr "Sélectionner appareil depuis la liste" #: platform/android/export/export.cpp +#, fuzzy msgid "Running on %s" -msgstr "" +msgstr "En cours d'exécution sur %s" #: platform/android/export/export.cpp -#, fuzzy msgid "Exporting APK..." -msgstr "Tout exporter" +msgstr "Exportation de l'APK..." #: platform/android/export/export.cpp -#, fuzzy msgid "Uninstalling..." -msgstr "Désinstaller" +msgstr "Désinstallation..." #: platform/android/export/export.cpp -#, fuzzy msgid "Installing to device, please wait..." -msgstr "Chargement en cours, veuillez patienter..." +msgstr "Installation sur l'appareil, veuillez patienter..." #: platform/android/export/export.cpp -#, fuzzy msgid "Could not install to device: %s" -msgstr "Impossible de démarrer le sous-processus !" +msgstr "Impossible d'installer sur l'appareil : %s" #: platform/android/export/export.cpp -#, fuzzy msgid "Running on device..." -msgstr "Lancement du script personnalisé…" +msgstr "En cours d'exécution sur l'appareil..." #: platform/android/export/export.cpp -#, fuzzy msgid "Could not execute on device." -msgstr "Impossible de créer le dossier." +msgstr "Impossible d'exécuter sur l'appareil." #: platform/android/export/export.cpp msgid "Unable to find the 'apksigner' tool." @@ -13509,46 +13442,49 @@ msgstr "" "est activée." #: platform/android/export/export.cpp +#, fuzzy msgid "" "'apksigner' could not be found.\n" "Please check the command is available in the Android SDK build-tools " "directory.\n" "The resulting %s is unsigned." msgstr "" +"Impossible de trouver 'apksigner'.\n" +"Veuillez vérifier que la commande est disponible dans le dossier build-tools " +"du SDK Android.\n" +"Le paquet sortant %s est non signé." #: platform/android/export/export.cpp +#, fuzzy msgid "Signing debug %s..." -msgstr "" +msgstr "Signature du debug %s..." #: platform/android/export/export.cpp -#, fuzzy msgid "Signing release %s..." -msgstr "" -"Analyse des fichiers en cours,\n" -"Veuillez patienter..." +msgstr "Signature de la version %s..." #: platform/android/export/export.cpp -#, fuzzy msgid "Could not find keystore, unable to export." -msgstr "Impossible d'ouvrir le modèle pour exportation :" +msgstr "Impossible de trouver le keystore, impossible d'exporter." #: platform/android/export/export.cpp +#, fuzzy msgid "'apksigner' returned with error #%d" -msgstr "" +msgstr "'apksigner' a terminé avec l'erreur #%d" #: platform/android/export/export.cpp #, fuzzy msgid "Verifying %s..." -msgstr "Ajout de %s..." +msgstr "Vérification de %s..." #: platform/android/export/export.cpp +#, fuzzy msgid "'apksigner' verification of %s failed." -msgstr "" +msgstr "La vérification de %s avec 'apksigner' a échoué." #: platform/android/export/export.cpp -#, fuzzy msgid "Exporting for Android" -msgstr "Tout exporter" +msgstr "Exportation vers Android" #: platform/android/export/export.cpp msgid "Invalid filename! Android App Bundle requires the *.aab extension." @@ -13570,7 +13506,7 @@ msgstr "" #: platform/android/export/export.cpp msgid "Unsupported export format!\n" -msgstr "" +msgstr "Format d'export non supporté !\n" #: platform/android/export/export.cpp msgid "" @@ -13594,20 +13530,20 @@ msgstr "" "Veuillez réinstaller la version d'Android depuis le menu 'Projet'." #: platform/android/export/export.cpp +#, fuzzy msgid "" "Unable to overwrite res://android/build/res/*.xml files with project name" msgstr "" +"Impossible d'écraser les fichiers res://android/build/res/*.xml avec le nom " +"du projet" #: platform/android/export/export.cpp -#, fuzzy msgid "Could not export project files to gradle project\n" -msgstr "" -"Impossible de modifier le fichier project.godot dans le chemin du projet." +msgstr "Impossible d'exporter les fichiers du projet vers le projet gradle\n" #: platform/android/export/export.cpp -#, fuzzy msgid "Could not write expansion package file!" -msgstr "Impossible d'écrire le fichier :" +msgstr "Impossible d'écrire le fichier du paquet d'expansion !" #: platform/android/export/export.cpp msgid "Building Android Project (gradle)" @@ -13638,37 +13574,42 @@ msgstr "" #: platform/android/export/export.cpp #, fuzzy msgid "Package not found: %s" -msgstr "Animation introuvable : « %s »" +msgstr "Paquet introuvable : « %s »" #: platform/android/export/export.cpp #, fuzzy msgid "Creating APK..." -msgstr "Création des contours..." +msgstr "Création du fichier APK..." #: platform/android/export/export.cpp -#, fuzzy msgid "" "Could not find template APK to export:\n" "%s" -msgstr "Impossible d'ouvrir le modèle pour exportation :" +msgstr "" +"Impossible de trouver le modèle de l'APK à exporter :\n" +"%s" #: platform/android/export/export.cpp +#, fuzzy msgid "" "Missing libraries in the export template for the selected architectures: " "%s.\n" "Please build a template with all required libraries, or uncheck the missing " "architectures in the export preset." msgstr "" +"Bibliothèques manquantes dans le modèle d'export pour les architectures " +"sélectionnées : %s.\n" +"Veuillez construire un modèle avec toutes les bibliothèques requises, ou " +"désélectionner les architectures manquantes dans le préréglage de l'export." #: platform/android/export/export.cpp #, fuzzy msgid "Adding files..." -msgstr "Ajout de %s..." +msgstr "Ajout de fichiers..." #: platform/android/export/export.cpp -#, fuzzy msgid "Could not export project files" -msgstr "Impossible d'écrire le fichier :" +msgstr "Impossible d'exporter les fichiers du projet" #: platform/android/export/export.cpp msgid "Aligning APK..." @@ -13676,7 +13617,7 @@ msgstr "Alignement de l'APK…" #: platform/android/export/export.cpp msgid "Could not unzip temporary unaligned APK." -msgstr "" +msgstr "Impossible de décompresser l'APK temporaire non aligné." #: platform/iphone/export/export.cpp platform/osx/export/export.cpp msgid "Identifier is missing." @@ -13723,9 +13664,8 @@ msgid "Could not write file:" msgstr "Impossible d'écrire le fichier :" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Could not read file:" -msgstr "Impossible d'écrire le fichier :" +msgstr "Impossible de lire le fichier :" #: platform/javascript/export/export.cpp #, fuzzy @@ -13733,14 +13673,12 @@ msgid "Could not read HTML shell:" msgstr "Impossible de lire le shell HTML personnalisé :" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Could not create HTTP server directory:" -msgstr "Impossible de créer le dossier." +msgstr "Impossible de créer le répertoire du serveur HTTP :" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Error starting HTTP server:" -msgstr "Erreur d'enregistrement de la scène." +msgstr "Erreur de démarrage du serveur HTTP :" #: platform/osx/export/export.cpp #, fuzzy @@ -13748,8 +13686,9 @@ msgid "Invalid bundle identifier:" msgstr "Identifiant invalide :" #: platform/osx/export/export.cpp +#, fuzzy msgid "Notarization: code signing required." -msgstr "" +msgstr "Certification : signature du code requise." #: platform/osx/export/export.cpp msgid "Notarization: hardened runtime required." @@ -14206,11 +14145,16 @@ msgstr "" "A la place utilisez une BakedLightMap." #: scene/3d/gi_probe.cpp +#, fuzzy msgid "" "The GIProbe Compress property has been deprecated due to known bugs and no " "longer has any effect.\n" "To remove this warning, disable the GIProbe's Compress property." msgstr "" +"La propriété GIProbe Compress a été déclarée comme obsolète à cause de bugs " +"connus et n'a plus aucun effet.\n" +"Pour supprimer cette avertissement, désactivez la propriété Compress du " +"GIProbe." #: scene/3d/light.cpp msgid "A SpotLight with an angle wider than 90 degrees cannot cast shadows." @@ -14304,15 +14248,15 @@ msgstr "Node A et Node B doivent être des PhysicsBody différents" #: scene/3d/portal.cpp msgid "The RoomManager should not be a child or grandchild of a Portal." -msgstr "" +msgstr "Le RoomManager ne doit pas être enfant ou grand-enfant d'un Portal." #: scene/3d/portal.cpp msgid "A Room should not be a child or grandchild of a Portal." -msgstr "" +msgstr "Un Room ne doit pas être enfant ou petit-enfant d'un Portal." #: scene/3d/portal.cpp msgid "A RoomGroup should not be a child or grandchild of a Portal." -msgstr "" +msgstr "Un RoomGroup ne doit pas être enfant ou petit-enfant d'un Portal." #: scene/3d/remote_transform.cpp msgid "" @@ -14362,6 +14306,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "Ce corps sera ignoré jusqu'à ce que vous définissiez un maillage." @@ -15125,9 +15105,6 @@ msgstr "Les constantes ne peuvent être modifiées." #~ msgid "Local Coords" #~ msgstr "Coordonnées locales" -#~ msgid "Snap Mode (%s)" -#~ msgstr "Mode d'aimantation (%s)" - #~ msgid "Tool Select" #~ msgstr "Outil sélection" diff --git a/editor/translations/ga.po b/editor/translations/ga.po index 98fe774878..872463b1a9 100644 --- a/editor/translations/ga.po +++ b/editor/translations/ga.po @@ -337,6 +337,7 @@ msgstr "" msgid "Remove Anim Track" msgstr "" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "" @@ -361,10 +362,26 @@ msgstr "Cruthaigh" msgid "Anim Insert" msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "CrannBeochan" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "property '%s'" +msgstr "" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "" @@ -920,7 +937,7 @@ msgstr "" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2214,6 +2231,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -2994,10 +3022,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "" @@ -3602,6 +3626,14 @@ msgid "Download from:" msgstr "" #: editor/export_template_manager.cpp +msgid "Open in Web Browser" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8288,6 +8320,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Remove All Color Items" msgstr "" @@ -8313,6 +8351,12 @@ msgid "Remove All StyleBox Items" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Add Color Item" msgstr "" @@ -11730,6 +11774,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13330,6 +13382,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/gl.po b/editor/translations/gl.po index 9b97e45e8a..054b62690d 100644 --- a/editor/translations/gl.po +++ b/editor/translations/gl.po @@ -6,18 +6,19 @@ # Andy Barcia <andybarcia4@gmail.com>, 2021. # PokeGalaico <abloodyfreaks@gmail.com>, 2021. # Kkai <kaieltroll@gmail.com>, 2021. +# davidrogel <david.rogel.pernas@icloud.com>, 2021. msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" -"PO-Revision-Date: 2021-04-26 22:32+0000\n" -"Last-Translator: Kkai <kaieltroll@gmail.com>\n" +"PO-Revision-Date: 2021-08-12 14:48+0000\n" +"Last-Translator: davidrogel <david.rogel.pernas@icloud.com>\n" "Language-Team: Galician <https://hosted.weblate.org/projects/godot-engine/" "godot/gl/>\n" "Language: gl\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8-bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.7-dev\n" +"X-Generator: Weblate 4.8-dev\n" #: core/math/expression.cpp modules/gdscript/gdscript_functions.cpp #: modules/visual_script/visual_script_builtin_funcs.cpp @@ -339,6 +340,7 @@ msgstr "Cambiar Modo de Bucle da Animación" msgid "Remove Anim Track" msgstr "Eliminar Pista de Animación" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Crear nova pista para %s e engadir chave?" @@ -363,10 +365,27 @@ msgstr "Crear" msgid "Anim Insert" msgstr "Engadir Animación" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animación" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "Un AnimationPlayer non pode animarse a si mesmo, só a outros players." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "Non existe a propiedade '%s'." + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Crear e Engadir Animación" @@ -943,9 +962,9 @@ msgstr "Crear Novo %s" msgid "No results for \"%s\"." msgstr "Non houbo resultado para \"%s\"." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." -msgstr "" +msgstr "Non hai descrición dispoñible para %s." #: editor/create_dialog.cpp editor/editor_file_dialog.cpp #: editor/filesystem_dock.cpp @@ -1140,7 +1159,7 @@ msgstr "Moitas grazas de parte da comunidade de Godot!" #: editor/editor_about.cpp editor/editor_node.cpp editor/project_manager.cpp msgid "Click to copy." -msgstr "" +msgstr "Clic para copiar." #: editor/editor_about.cpp msgid "Godot Engine contributors" @@ -1724,7 +1743,7 @@ msgstr "Importación" #: editor/editor_feature_profile.cpp msgid "Allows to view and edit 3D scenes." -msgstr "" +msgstr "Permite ver e editar escenas 3D." #: editor/editor_feature_profile.cpp msgid "Allows to edit scripts using the integrated script editor." @@ -2293,6 +2312,17 @@ msgid "New Window" msgstr "Nova Xanela" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "Xira cando o editor actualiza a pantalla." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Os recursos importados non se poden gardar." @@ -3161,10 +3191,6 @@ msgid "Save & Restart" msgstr "Gardar e Reinicar" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "Xira cando o editor actualiza a pantalla." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "Actualizar de Maneira Continua" @@ -3797,6 +3823,15 @@ msgid "Download from:" msgstr "Erro na Descarga" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Abrir no Explorador de Arquivos" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8606,6 +8641,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Eliminar Tódolos Marcapáxinas" @@ -8636,6 +8677,12 @@ msgid "Remove All StyleBox Items" msgstr "Eliminar Tódolos Marcapáxinas" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Engadir Elemento" @@ -12185,6 +12232,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13860,6 +13915,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "Este corpo será ignorado ata que se lle sea asignado unha malla." diff --git a/editor/translations/he.po b/editor/translations/he.po index bbe0ebbe08..d0a09565de 100644 --- a/editor/translations/he.po +++ b/editor/translations/he.po @@ -355,6 +355,7 @@ msgstr "×©×™× ×•×™ מצב לול×ת ×”× ×¤×©×”" msgid "Remove Anim Track" msgstr "מחיקת רצועת ×”× ×¤×©×”" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "×”×× ×œ×™×¦×•×¨ רצועה חדשה ל%s ×•×œ×”×›× ×™×¡ מפתח?" @@ -379,10 +380,28 @@ msgstr "יצירה" msgid "Anim Insert" msgstr "הוסף ×”× ×¤×©×”" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "מצב הצמדה (%s)" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "×”× ×¤×©×”" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "× ×’×Ÿ ×”×”× ×¤×©×•×ª ×œ× ×™×›×•×œ ×œ×”× ×¤×™×© ×ת עצמו, רק ×©×—×§× ×™× ×חרי×." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "×œ× ×§×™×™× ×ž×פיין ‚%s’." + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "יצירה והוספה של ×”× ×¤×©×”" @@ -960,7 +979,7 @@ msgstr "יצירת %s חדש" msgid "No results for \"%s\"." msgstr "×ין תוצ×ות עבור \"%s\"." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2309,6 +2328,17 @@ msgid "New Window" msgstr "חלון חדש" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "מסתובב ×›×שר חלון העורך מצויר מחדש." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "מש××‘×™× ×ž×™×•×‘××™× ×œ× × ×©×ž×¨×•." @@ -3143,10 +3173,6 @@ msgid "Save & Restart" msgstr "שמירה והפעלה מחדש" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "מסתובב ×›×שר חלון העורך מצויר מחדש." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "עדכון רציף" @@ -3789,6 +3815,16 @@ msgid "Download from:" msgstr "שגי×ת הורדה" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "הפעלה בדפדפן" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "שגי×ת העתקה" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8747,6 +8783,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "הסרת כל × ×§×•×“×•×ª העצירה" @@ -8777,6 +8819,12 @@ msgid "Remove All StyleBox Items" msgstr "הסרת כל × ×§×•×“×•×ª העצירה" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "מועדפי×:" @@ -12388,6 +12436,14 @@ msgstr "×©×™× ×•×™ גובה לצורת גליל" msgid "Change Ray Shape Length" msgstr "×©×™× ×•×™ ×ורך לצורת קרן" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "×©×™× ×•×™ רדיוס גליל" @@ -14113,6 +14169,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "תהיה התעלמות מגוף ×–×” עד שתקבע רשת." @@ -14643,9 +14735,6 @@ msgstr "××™ ×פשר ×œ×©× ×•×ª קבועי×." #~ msgid "Local Coords" #~ msgstr "× ×§×•×“×•×ª ציון מקומיות" -#~ msgid "Snap Mode (%s)" -#~ msgstr "מצב הצמדה (%s)" - #~ msgid "Project List" #~ msgstr "רשימת המיזמי×" diff --git a/editor/translations/hi.po b/editor/translations/hi.po index a4e7d4ae72..916e6fd01d 100644 --- a/editor/translations/hi.po +++ b/editor/translations/hi.po @@ -348,6 +348,7 @@ msgstr "à¤à¤¨à¤¿à¤®à¥‡à¤¶à¤¨ लूप मोड बदलें" msgid "Remove Anim Track" msgstr "अनीम टà¥à¤°à¥ˆà¤• निकालें" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "% à¤à¤¸ के लिठनया टà¥à¤°à¥ˆà¤• बनाà¤à¤‚ और कà¥à¤‚जी डालें?" @@ -372,10 +373,27 @@ msgstr "बनाना" msgid "Anim Insert" msgstr "अनीम डालें" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "कारà¥à¤¯à¥‹à¤‚:" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "à¤à¤¨à¤¿à¤®à¥‡à¤¶à¤¨à¤ªà¥à¤²à¥‡à¤¯à¤° खà¥à¤¦ को चेतन नहीं कर सकता, केवल अनà¥à¤¯ खिलाड़ी।" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "पà¥à¤°à¥‹à¤ªà¤°à¥à¤Ÿà¥€" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "अनीम बनाà¤à¤‚ और डालें" @@ -947,7 +965,7 @@ msgstr "नया%s बनाà¤à¤‚" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2289,6 +2307,17 @@ msgid "New Window" msgstr "नया विंडो" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "जब संपादक खिड़की फिर से खींचता है तो सà¥à¤ªà¤¿à¤¨ करता है।" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "इंपोरà¥à¤Ÿà¥‡à¤¡ रेसोरà¥à¤¸à¥‡à¤¸ सेव नहीं कर सकते." @@ -3130,10 +3159,6 @@ msgid "Save & Restart" msgstr "सहेजें और पà¥à¤¨à¤ƒ आरंठकरें" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "जब संपादक खिड़की फिर से खींचता है तो सà¥à¤ªà¤¿à¤¨ करता है।" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "लगातार अपडेट करें" @@ -3785,6 +3810,15 @@ msgid "Download from:" msgstr "डाउनलोड" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "फ़ाइल मैनेजर में खोलिये" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8561,6 +8595,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "आइटम निकालें" @@ -8591,6 +8631,12 @@ msgid "Remove All StyleBox Items" msgstr "आइटम निकालें" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "पसंदीदा में जोड़ें" @@ -12124,6 +12170,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13771,6 +13825,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/hr.po b/editor/translations/hr.po index ea48ef0246..37d517cba0 100644 --- a/editor/translations/hr.po +++ b/editor/translations/hr.po @@ -340,6 +340,7 @@ msgstr "Promijeni NaÄin Ponavljanja Animacije" msgid "Remove Anim Track" msgstr "Ukloni Stazu Animacije" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Stvori NOVU stazu za %s i umetni kljuÄ?" @@ -364,10 +365,26 @@ msgstr "Stvori" msgid "Anim Insert" msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animacija" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "Animator ne može animirati sebe, samo druge animatore." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "property '%s'" +msgstr "" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Anim Stvori & Umetni" @@ -927,7 +944,7 @@ msgstr "Napravi novi %s" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2240,6 +2257,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -3021,10 +3049,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "Kontinuirano ažuriraj" @@ -3635,6 +3659,15 @@ msgid "Download from:" msgstr "" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Otvori u Inspektoru" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8337,6 +8370,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Remove All Color Items" msgstr "" @@ -8364,6 +8403,12 @@ msgid "Remove All StyleBox Items" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Add Color Item" msgstr "" @@ -11805,6 +11850,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13413,6 +13466,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/hu.po b/editor/translations/hu.po index 3af983ff18..c822f5bd53 100644 --- a/editor/translations/hu.po +++ b/editor/translations/hu.po @@ -351,6 +351,7 @@ msgstr "Animáció Összefűzés Módjának Változtatása" msgid "Remove Anim Track" msgstr "Animáció Sáv EltávolÃtása" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Létrehoz ÚJ sávot a következÅ‘höz: %s, és beszúrja a kulcsot?" @@ -375,12 +376,29 @@ msgstr "Létrehozás" msgid "Anim Insert" msgstr "Animáció - Beszúrás" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animáció" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" "AnimationPlayer nem tudja önmagát animálni, csak más AnimationPlayer " "elemeket." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "Tulajdonság" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Animáció - Létrehozás és Beillesztés" @@ -959,7 +977,7 @@ msgstr "Új %s létrehozása" msgid "No results for \"%s\"." msgstr "Nincs találat a következÅ‘re: \"%s\"." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2309,6 +2327,17 @@ msgid "New Window" msgstr "Új ablak" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "Pörög, amikor a szerkesztÅ‘ablak újrarajzolódik." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Az importált erÅ‘források nem menthetÅ‘k." @@ -3178,10 +3207,6 @@ msgid "Save & Restart" msgstr "Mentés és újraindÃtás" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "Pörög, amikor a szerkesztÅ‘ablak újrarajzolódik." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "Folyamatos frissÃtés" @@ -3825,6 +3850,16 @@ msgid "Download from:" msgstr "Letöltési Hiba" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Megnyitás a FájlkezelÅ‘ben" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "Hiba Másolása" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8609,6 +8644,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Összes könyvjelzÅ‘ eltávolÃtása" @@ -8639,6 +8680,12 @@ msgid "Remove All StyleBox Items" msgstr "Összes könyvjelzÅ‘ eltávolÃtása" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Elem Hozzáadása" @@ -12104,6 +12151,16 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "Görbe Pont PozÃció BeállÃtása" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "Görbe Pont PozÃció BeállÃtása" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13734,6 +13791,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/id.po b/editor/translations/id.po index ccc5865b85..3426bd0962 100644 --- a/editor/translations/id.po +++ b/editor/translations/id.po @@ -368,6 +368,7 @@ msgstr "Ubah Mode Perulangan Animasi" msgid "Remove Anim Track" msgstr "Hapus Trek Anim" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Buat track BARU untuk %s dan masukkan tombol?" @@ -392,11 +393,29 @@ msgstr "Buat" msgid "Anim Insert" msgstr "Sisipkan Anim" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "Tidak dapat membuka '%s'." + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animasi" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" "AnimationPlayer tidak bisa menganimasikan diri sendiri, gunakan pemutar lain." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "Tidak ada properti '%s'." + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Buat & Sisipkan Anim" @@ -972,7 +991,7 @@ msgstr "Buat %s baru" msgid "No results for \"%s\"." msgstr "Tidak ada hasil untuk \"%s\"." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2322,6 +2341,17 @@ msgid "New Window" msgstr "Jendela Baru" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "Putar ketika jendela editor digambar ulang." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Resource yang diimpor tidak dapat disimpan." @@ -3180,10 +3210,6 @@ msgid "Save & Restart" msgstr "Simpan & Mulai Ulang" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "Putar ketika jendela editor digambar ulang." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "Perbarui Terus-menerus" @@ -3845,6 +3871,16 @@ msgid "Download from:" msgstr "Unduhan Gagal" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Jalankan di Peramban" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "Salin Galat" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8712,6 +8748,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Hapus Semua Item" @@ -8742,6 +8784,12 @@ msgid "Remove All StyleBox Items" msgstr "Hapus Semua Item" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Tambah Item Kelas" @@ -12412,6 +12460,16 @@ msgstr "Ubah Tinggi Bentuk Silinder" msgid "Change Ray Shape Length" msgstr "Ubah Panjang Shape Ray" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "Atur Posisi Titik Kurva" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "Atur Posisi Titik Kurva" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "Ubah Radius Silinder" @@ -14186,6 +14244,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "Body ini akan diabaikan hingga Anda mengatur mesh-nya." @@ -15242,9 +15336,6 @@ msgstr "Konstanta tidak dapat dimodifikasi." #~ msgid "I see..." #~ msgstr "Mengerti..." -#~ msgid "Can't open '%s'." -#~ msgstr "Tidak dapat membuka '%s'." - #~ msgid "Ugh" #~ msgstr "Duh" diff --git a/editor/translations/is.po b/editor/translations/is.po index 916be97fb4..e536b0a8f6 100644 --- a/editor/translations/is.po +++ b/editor/translations/is.po @@ -360,6 +360,7 @@ msgstr "" msgid "Remove Anim Track" msgstr "Fjarlægja Anim track" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "" @@ -384,10 +385,26 @@ msgstr "" msgid "Anim Insert" msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Stillið breyting á:" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "property '%s'" +msgstr "" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "" @@ -954,7 +971,7 @@ msgstr "" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2253,6 +2270,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -3036,10 +3064,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp #, fuzzy msgid "Update Continuously" msgstr "Samfellt" @@ -3648,6 +3672,15 @@ msgid "Download from:" msgstr "" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Opna Verkefna Stjóra?" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8376,6 +8409,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Remove All Color Items" msgstr "" @@ -8403,6 +8442,12 @@ msgid "Remove All StyleBox Items" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Add Color Item" msgstr "" @@ -11873,6 +11918,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13487,6 +13540,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/it.po b/editor/translations/it.po index cec4cf1beb..c3aa84d4b6 100644 --- a/editor/translations/it.po +++ b/editor/translations/it.po @@ -37,7 +37,7 @@ # Stefano Merazzi <asso99@hotmail.com>, 2019. # Sinapse X <sinapsex13@gmail.com>, 2019. # Micila Micillotto <micillotto@gmail.com>, 2019, 2020, 2021. -# Mirko Soppelsa <miknsop@gmail.com>, 2019, 2020. +# Mirko Soppelsa <miknsop@gmail.com>, 2019, 2020, 2021. # No <kingofwizards.kw7@gmail.com>, 2019. # StarFang208 <polaritymanx@yahoo.it>, 2019. # Katia Piazza <gydey@ridiculousglitch.com>, 2019. @@ -64,8 +64,8 @@ msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2021-07-26 14:18+0000\n" -"Last-Translator: Riteo Siuga <riteo@posteo.net>\n" +"PO-Revision-Date: 2021-08-10 21:39+0000\n" +"Last-Translator: Mirko <miknsop@gmail.com>\n" "Language-Team: Italian <https://hosted.weblate.org/projects/godot-engine/" "godot/it/>\n" "Language: it\n" @@ -73,7 +73,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.7.2-dev\n" +"X-Generator: Weblate 4.8-dev\n" #: core/math/expression.cpp modules/gdscript/gdscript_functions.cpp #: modules/visual_script/visual_script_builtin_funcs.cpp @@ -394,6 +394,7 @@ msgstr "Cambia la modalità del ciclo di un'animazione" msgid "Remove Anim Track" msgstr "Rimuovi una traccia d'animazione" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Creare una NUOVA traccia per %s e inserire la chiave?" @@ -418,10 +419,28 @@ msgstr "Crea" msgid "Anim Insert" msgstr "Inserisci un'animazione" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "Impossibile aprire '%s'." + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animazione" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "AnimationPlayer non può animare se stesso, solo altri nodi." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "Non esiste nessuna proprietà \"%s\"." + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Crea un'animazione e inserisci un fotogramma chiave" @@ -1005,7 +1024,7 @@ msgstr "Crea un nuovo %s" msgid "No results for \"%s\"." msgstr "Nessun risultato per \"%s\"." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2372,6 +2391,17 @@ msgid "New Window" msgstr "Nuova Finestra" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "Gira quando la finestra dell'editor viene ridisegnata." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Le risorse importate non possono essere salvate." @@ -3248,10 +3278,6 @@ msgid "Save & Restart" msgstr "Salva e riavvia" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "Gira quando la finestra dell'editor viene ridisegnata." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "Aggiorna continuamente" @@ -3499,7 +3525,6 @@ msgid "Inclusive" msgstr "Inclusivo" #: editor/editor_profiler.cpp -#, fuzzy msgid "Self" msgstr "Se stesso" @@ -3930,6 +3955,16 @@ msgid "Download from:" msgstr "Errore durante il download" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Esegui nel Browser" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "Copia Errore" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -7908,9 +7943,8 @@ msgid "Translate" msgstr "Trasla:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Scale" -msgstr "Scala:" +msgstr "Scala" #: editor/plugins/spatial_editor_plugin.cpp msgid "Scaling: " @@ -8855,6 +8889,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Rimuovi tutti gli elementi" @@ -8885,6 +8925,12 @@ msgid "Remove All StyleBox Items" msgstr "Rimuovi tutti gli elementi" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Aggiungi Elementi di Classe" @@ -12574,6 +12620,16 @@ msgstr "Modifica Altezza di Forma del Cilindro" msgid "Change Ray Shape Length" msgstr "Cambia lunghezza Ray Shape" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "Imposta Posizione Punto Curva" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "Imposta Posizione Punto Curva" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "Modifica Raggio del Cilindro" @@ -13569,7 +13625,7 @@ msgstr "" #: platform/android/export/export.cpp #, fuzzy msgid "Exporting for Android" -msgstr "Esportando Tutto" +msgstr "Esportazione per Android" #: platform/android/export/export.cpp msgid "Invalid filename! Android App Bundle requires the *.aab extension." @@ -14377,6 +14433,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "Questo corpo verrà ignorato fino a quando non imposterai una mesh." @@ -15641,9 +15733,6 @@ msgstr "Le constanti non possono essere modificate." #~ msgid "I see..." #~ msgstr "Capisco..." -#~ msgid "Can't open '%s'." -#~ msgstr "Impossibile aprire '%s'." - #~ msgid "Ugh" #~ msgstr "Ugh" diff --git a/editor/translations/ja.po b/editor/translations/ja.po index 83e544e5b5..3ee6d0b49d 100644 --- a/editor/translations/ja.po +++ b/editor/translations/ja.po @@ -15,7 +15,7 @@ # Tohru Ike (rokujyouhitoma) <rokujyouhitomajp@gmail.com>, 2017-2018. # yu tang <0011solo@gmail.com>, 2018. # zukkun <zukkun@gmail.com>, 2018. -# sugusan <sugusan.development@gmail.com>, 2018, 2019. +# sugusan <sugusan.development@gmail.com>, 2018, 2019, 2021. # Nathan Lovato <nathan.lovato.art@gmail.com>, 2018. # nyanode <akaruooyagi@yahoo.co.jp>, 2018. # nitenook <admin@alterbaum.net>, 2018, 2019, 2020, 2021. @@ -37,8 +37,8 @@ msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2021-07-29 02:33+0000\n" -"Last-Translator: nitenook <admin@alterbaum.net>\n" +"PO-Revision-Date: 2021-08-12 14:48+0000\n" +"Last-Translator: sugusan <sugusan.development@gmail.com>\n" "Language-Team: Japanese <https://hosted.weblate.org/projects/godot-engine/" "godot/ja/>\n" "Language: ja\n" @@ -46,7 +46,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -"X-Generator: Weblate 4.7.2-dev\n" +"X-Generator: Weblate 4.8-dev\n" #: core/math/expression.cpp modules/gdscript/gdscript_functions.cpp #: modules/visual_script/visual_script_builtin_funcs.cpp @@ -367,6 +367,7 @@ msgstr "アニメーションã®ãƒ«ãƒ¼ãƒ—モードを変更" msgid "Remove Anim Track" msgstr "アニメーショントラックを除去" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "%s ã®æ–°è¦ãƒˆãƒ©ãƒƒã‚¯ã‚’作æˆã—ã€ã‚ーを挿入ã—ã¾ã™ã‹ï¼Ÿ" @@ -391,12 +392,30 @@ msgstr "作æˆ" msgid "Anim Insert" msgstr "アニメーション挿入" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "'..'を処ç†ã§ãã¾ã›ã‚“" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "アニメーション" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" "アニメーションプレイヤーã¯ä»–ã®ãƒ—レイヤーã ã‘をアニメーション化ã™ã‚‹ã“ã¨ã¯ã§ã" "ã¾ã›ã‚“。" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "プãƒãƒ‘ティ '%s' ã¯å˜åœ¨ã—ã¾ã›ã‚“。" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "アニメーションã®ä½œæˆã¨æŒ¿å…¥" @@ -972,9 +991,9 @@ msgstr "%s ã‚’æ–°è¦ä½œæˆ" msgid "No results for \"%s\"." msgstr "\"%s\" ã®çµæžœã¯ã‚りã¾ã›ã‚“。" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." -msgstr "" +msgstr "%s ã«ã¤ã„ã¦ã®èª¬æ˜Žã¯ã‚りã¾ã›ã‚“。" #: editor/create_dialog.cpp editor/editor_file_dialog.cpp #: editor/filesystem_dock.cpp @@ -1166,7 +1185,7 @@ msgstr "Godot コミュニティより感è¬ã‚’ï¼" #: editor/editor_about.cpp editor/editor_node.cpp editor/project_manager.cpp msgid "Click to copy." -msgstr "" +msgstr "クリックã—ã¦ã‚³ãƒ”ーã—ã¾ã™ã€‚" #: editor/editor_about.cpp msgid "Godot Engine contributors" @@ -2325,6 +2344,17 @@ msgid "New Window" msgstr "æ–°è¦ã‚¦ã‚£ãƒ³ãƒ‰ã‚¦" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "エディタ ウィンドウã®å†æç”»æ™‚ã«ã‚¹ãƒ”ンã—ã¾ã™ã€‚" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "インãƒãƒ¼ãƒˆã—ãŸãƒªã‚½ãƒ¼ã‚¹ã¯ä¿å˜ã§ãã¾ã›ã‚“。" @@ -3185,10 +3215,6 @@ msgid "Save & Restart" msgstr "ä¿å˜ã—ã¦å†èµ·å‹•" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "エディタ ウィンドウã®å†æç”»æ™‚ã«ã‚¹ãƒ”ンã—ã¾ã™ã€‚" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "ç¶™ç¶šçš„ã«æ›´æ–°" @@ -3634,11 +3660,11 @@ msgstr "ノードã‹ã‚‰ã‚¤ãƒ³ãƒãƒ¼ãƒˆ:" #: editor/export_template_manager.cpp msgid "Open the folder containing these templates." -msgstr "" +msgstr "ã“れらã®ãƒ†ãƒ³ãƒ—レートãŒã‚るフォルダを開ãã¾ã™ã€‚" #: editor/export_template_manager.cpp msgid "Uninstall these templates." -msgstr "" +msgstr "ã“れらã®ãƒ†ãƒ³ãƒ—レートをアンインストールã—ã¾ã™ã€‚" #: editor/export_template_manager.cpp #, fuzzy @@ -3652,7 +3678,7 @@ msgstr "ミラーをå–å¾—ã—ã¦ã„ã¾ã™ã€‚ã—ã°ã‚‰ããŠå¾…ã¡ãã ã•ã„... #: editor/export_template_manager.cpp msgid "Starting the download..." -msgstr "" +msgstr "ダウンãƒãƒ¼ãƒ‰ã‚’é–‹å§‹ã—ã¦ã„ã¾ã™..." #: editor/export_template_manager.cpp msgid "Error requesting URL:" @@ -3695,7 +3721,7 @@ msgstr "リクエストã¯å¤±æ•—ã—ã¾ã—ãŸã€‚" #: editor/export_template_manager.cpp msgid "Download complete; extracting templates..." -msgstr "" +msgstr "ダウンãƒãƒ¼ãƒ‰ãŒå®Œäº†ã—ã¾ã—ãŸã€‚テンプレートを展開ã—ã¦ã„ã¾ã™..." #: editor/export_template_manager.cpp msgid "Cannot remove temporary file:" @@ -3820,10 +3846,12 @@ msgstr "ç¾åœ¨ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³:" #: editor/export_template_manager.cpp msgid "Export templates are missing. Download them or install from a file." msgstr "" +"エクスãƒãƒ¼ãƒˆ テンプレートãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“。ダウンãƒãƒ¼ãƒ‰ã™ã‚‹ã‹ãƒ•ァイルã‹ã‚‰ã‚¤ãƒ³" +"ストールã—ã¦ãã ã•ã„。" #: editor/export_template_manager.cpp msgid "Export templates are installed and ready to be used." -msgstr "" +msgstr "エクスãƒãƒ¼ãƒˆ テンプレートã¯ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã•れã¦ãŠã‚Šã€åˆ©ç”¨ã§ãã¾ã™ã€‚" #: editor/export_template_manager.cpp #, fuzzy @@ -3849,8 +3877,18 @@ msgid "Download from:" msgstr "ダウンãƒãƒ¼ãƒ‰ã‚¨ãƒ©ãƒ¼" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "ブラウザã§å®Ÿè¡Œ" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "エラーをコピー" + +#: editor/export_template_manager.cpp msgid "Download and Install" -msgstr "" +msgstr "ダウンãƒãƒ¼ãƒ‰ã—ã¦ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«" #: editor/export_template_manager.cpp msgid "" @@ -4052,35 +4090,32 @@ msgid "Collapse All" msgstr "ã™ã¹ã¦æŠ˜ã‚ŠãŸãŸã‚€" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort files" -msgstr "ファイル検索" +msgstr "ファイルã®ä¸¦ã³æ›¿ãˆ" #: editor/filesystem_dock.cpp msgid "Sort by Name (Ascending)" -msgstr "" +msgstr "åå‰ (æ˜‡é †) ã§ä¸¦ã³æ›¿ãˆ" #: editor/filesystem_dock.cpp msgid "Sort by Name (Descending)" -msgstr "" +msgstr "åå‰ (é™é †) ã§ä¸¦ã³æ›¿ãˆ" #: editor/filesystem_dock.cpp msgid "Sort by Type (Ascending)" -msgstr "" +msgstr "種類 (æ˜‡é †) ã§ä¸¦ã³æ›¿ãˆ" #: editor/filesystem_dock.cpp msgid "Sort by Type (Descending)" -msgstr "" +msgstr "種類 (é™é †) ã§ä¸¦ã³æ›¿ãˆ" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort by Last Modified" -msgstr "最終更新" +msgstr "æ›´æ–°æ—¥æ™‚ãŒæ–°ã—ã„é †ã§ä¸¦ã³æ›¿ãˆ" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort by First Modified" -msgstr "最終更新" +msgstr "更新日時ãŒå¤ã„é †ã§ä¸¦ã³æ›¿ãˆ" #: editor/filesystem_dock.cpp msgid "Duplicate..." @@ -8708,6 +8743,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "ã™ã¹ã¦ã®ã‚¢ã‚¤ãƒ†ãƒ を除去" @@ -8738,6 +8779,12 @@ msgid "Remove All StyleBox Items" msgstr "ã™ã¹ã¦ã®ã‚¢ã‚¤ãƒ†ãƒ を除去" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "ã‚¯ãƒ©ã‚¹ã‚¢ã‚¤ãƒ†ãƒ è¿½åŠ " @@ -12390,6 +12437,16 @@ msgstr "円柱シェイプã®é«˜ã•を変更" msgid "Change Ray Shape Length" msgstr "レイシェイプã®é•·ã•を変更" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "カーブãƒã‚¤ãƒ³ãƒˆã®ä½ç½®ã‚’è¨å®š" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "カーブãƒã‚¤ãƒ³ãƒˆã®ä½ç½®ã‚’è¨å®š" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "円柱ã®åŠå¾„を変更" @@ -14160,6 +14217,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "ã“ã®ãƒœãƒ‡ã‚£ã¯ã€ãƒ¡ãƒƒã‚·ãƒ¥ã‚’è¨å®šã™ã‚‹ã¾ã§ç„¡è¦–ã•れã¾ã™ã€‚" @@ -15444,10 +15537,6 @@ msgstr "定数ã¯å¤‰æ›´ã§ãã¾ã›ã‚“。" #~ msgstr "ã‚ã‹ã£ãŸ..." #, fuzzy -#~ msgid "Can't open '%s'." -#~ msgstr "'..'を処ç†ã§ãã¾ã›ã‚“" - -#, fuzzy #~ msgid "Ugh" #~ msgstr "ã†ã‡" diff --git a/editor/translations/ka.po b/editor/translations/ka.po index 144ef77158..7abc89b216 100644 --- a/editor/translations/ka.po +++ b/editor/translations/ka.po @@ -366,6 +366,7 @@ msgstr "áƒáƒœáƒ˜áƒ› ლუპის შეცვლáƒ" msgid "Remove Anim Track" msgstr "áƒáƒœáƒ˜áƒ›áƒáƒªáƒ˜áƒ˜áƒ¡ თრექის წáƒáƒ¨áƒšáƒ" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp #, fuzzy msgid "Create NEW track for %s and insert key?" @@ -391,6 +392,17 @@ msgstr "შექმნáƒ" msgid "Anim Insert" msgstr "áƒáƒœáƒ˜áƒ› ჩáƒáƒ§áƒ”ნებáƒ" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "ფუნქციები:" + #: editor/animation_track_editor.cpp #, fuzzy msgid "AnimationPlayer can't animate itself, only other players." @@ -398,6 +410,12 @@ msgstr "" "áƒáƒœáƒ˜áƒ›áƒáƒªáƒ˜áƒ˜áƒ¡ გáƒáƒ›áƒ¨áƒ•ები ვერჩáƒáƒáƒ¢áƒáƒ ებს ცდებს სáƒáƒ™áƒ£áƒ—áƒáƒ თáƒáƒ•ზე, მხáƒáƒšáƒáƒ“ სხვრ" "მáƒáƒ—áƒáƒ›áƒáƒ¨áƒ”ებზე." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "áƒáƒ‘იექტზე დáƒáƒ™áƒ•ირვებáƒ" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "áƒáƒœáƒ˜áƒ› შექმნრ& ჩáƒáƒ§áƒ”ნებáƒ" @@ -988,7 +1006,7 @@ msgstr "áƒáƒ®áƒáƒšáƒ˜ %s შექმნáƒ" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2331,6 +2349,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -3120,10 +3149,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp #, fuzzy msgid "Update Continuously" msgstr "უწყვეტი" @@ -3741,6 +3766,15 @@ msgid "Download from:" msgstr "" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "გáƒáƒ®áƒ¡áƒœáƒ˜áƒšáƒ˜" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8578,6 +8612,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "სáƒáƒ§áƒ•áƒáƒ ლები:" @@ -8606,6 +8646,12 @@ msgid "Remove All StyleBox Items" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "სáƒáƒ§áƒ•áƒáƒ ლები:" @@ -12141,6 +12187,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13772,6 +13826,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/km.po b/editor/translations/km.po index e18a778132..187307bc17 100644 --- a/editor/translations/km.po +++ b/editor/translations/km.po @@ -337,6 +337,7 @@ msgstr "" msgid "Remove Anim Track" msgstr "" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "" @@ -361,10 +362,25 @@ msgstr "" msgid "Anim Insert" msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "animation" +msgstr "" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "property '%s'" +msgstr "" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "" @@ -919,7 +935,7 @@ msgstr "" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2210,6 +2226,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -2989,10 +3016,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "" @@ -3597,6 +3620,14 @@ msgid "Download from:" msgstr "" #: editor/export_template_manager.cpp +msgid "Open in Web Browser" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8277,6 +8308,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Remove All Color Items" msgstr "" @@ -8301,6 +8338,12 @@ msgid "Remove All StyleBox Items" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Add Color Item" msgstr "" @@ -11708,6 +11751,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13302,6 +13353,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/ko.po b/editor/translations/ko.po index eda096e4ce..1f24eb1b1d 100644 --- a/editor/translations/ko.po +++ b/editor/translations/ko.po @@ -27,7 +27,7 @@ msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2021-07-16 05:47+0000\n" +"PO-Revision-Date: 2021-08-12 14:48+0000\n" "Last-Translator: Myeongjin Lee <aranet100@gmail.com>\n" "Language-Team: Korean <https://hosted.weblate.org/projects/godot-engine/" "godot/ko/>\n" @@ -36,7 +36,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -"X-Generator: Weblate 4.7.2-dev\n" +"X-Generator: Weblate 4.8-dev\n" #: core/math/expression.cpp modules/gdscript/gdscript_functions.cpp #: modules/visual_script/visual_script_builtin_funcs.cpp @@ -358,6 +358,7 @@ msgstr "ì• ë‹ˆë©”ì´ì…˜ 루프 모드 바꾸기" msgid "Remove Anim Track" msgstr "ì• ë‹ˆë©”ì´ì…˜ 트랙 ì‚ì œ" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "%sì„(를) 위해 새 íŠ¸ëž™ì„ ë§Œë“¤ê³ í‚¤ë¥¼ ì‚½ìž…í• ê¹Œìš”?" @@ -382,12 +383,30 @@ msgstr "만들기" msgid "Anim Insert" msgstr "ì• ë‹ˆë©”ì´ì…˜ 삽입" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "'%s' 열수 ì—†ìŒ." + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "ì• ë‹ˆë©”ì´ì…˜" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" "AnimationPlayer는 ìžì‹ ì´ ì•„ë‹Œ 다른 í”Œë ˆì´ì–´ì—ë§Œ ì• ë‹ˆë©”ì´ì…˜ì„ ë¶€ì—¬í• ìˆ˜ 있습니" "다." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "'%s' ì†ì„±ì´ 없습니다." + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "ì• ë‹ˆë©”ì´ì…˜ 만들기 & 삽입" @@ -961,9 +980,9 @@ msgstr "새 %s 만들기" msgid "No results for \"%s\"." msgstr "\"%s\"ì— ëŒ€í•œ 결과가 없습니다." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." -msgstr "" +msgstr "%sì˜ ì‚¬ìš© 가능한 ì„¤ëª…ì´ ì—†ìŠµë‹ˆë‹¤." #: editor/create_dialog.cpp editor/editor_file_dialog.cpp #: editor/filesystem_dock.cpp @@ -1263,11 +1282,11 @@ msgstr "%s (ì´ë¯¸ 존재함)" #: editor/editor_asset_installer.cpp msgid "Contents of asset \"%s\" - %d file(s) conflict with your project:" -msgstr "" +msgstr "ì• ì…‹ \"%s\"ì˜ ë‚´ìš© - íŒŒì¼ %d개가 프로ì 트와 ì¶©ëŒí•©ë‹ˆë‹¤:" #: editor/editor_asset_installer.cpp msgid "Contents of asset \"%s\" - No files conflict with your project:" -msgstr "" +msgstr "ì• ì…‹ \"%s\"ì˜ ë‚´ìš© - 프로ì 트와 ì¶©ëŒí•˜ëŠ” 파ì¼ì´ 없습니다:" #: editor/editor_asset_installer.cpp msgid "Uncompressing Assets" @@ -1279,9 +1298,8 @@ msgid "The following files failed extraction from asset \"%s\":" msgstr "ë‹¤ìŒ íŒŒì¼ì„ 패키지ì—서 ì¶”ì¶œí•˜ëŠ”ë° ì‹¤íŒ¨í•¨:" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "(and %s more files)" -msgstr "외 %d ê°œì˜ íŒŒì¼." +msgstr "(ë° ë” ë§Žì€ íŒŒì¼ %sê°œ)" #: editor/editor_asset_installer.cpp #, fuzzy @@ -1531,13 +1549,12 @@ msgid "Can't add autoload:" msgstr "ì˜¤í† ë¡œë“œë¥¼ ì¶”ê°€í• ìˆ˜ ì—†ìŒ:" #: editor/editor_autoload_settings.cpp -#, fuzzy msgid "%s is an invalid path. File does not exist." -msgstr "파ì¼ì´ 존재하지 않습니다." +msgstr "%s는 ìž˜ëª»ëœ ê²½ë¡œìž…ë‹ˆë‹¤. 파ì¼ì´ 존재하지 않습니다." #: editor/editor_autoload_settings.cpp msgid "%s is an invalid path. Not in resource path (res://)." -msgstr "" +msgstr "%s는 ìž˜ëª»ëœ ê²½ë¡œìž…ë‹ˆë‹¤. 리소스 경로(res://)ì— ìžˆì§€ 않습니다." #: editor/editor_autoload_settings.cpp msgid "Add AutoLoad" @@ -1814,9 +1831,8 @@ msgid "Class Properties:" msgstr "ì†ì„±:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Main Features:" -msgstr "기능" +msgstr "주요 기능:" #: editor/editor_feature_profile.cpp #, fuzzy @@ -1876,14 +1892,12 @@ msgid "Export" msgstr "내보내기" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Configure Selected Profile:" -msgstr "현재 프로필:" +msgstr "ì„ íƒëœ 프로필 구성:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Extra Options:" -msgstr "í…ìŠ¤ì³ ì˜µì…˜" +msgstr "별ë„ì˜ ì˜µì…˜:" #: editor/editor_feature_profile.cpp msgid "Create or import a profile to edit available classes and properties." @@ -2307,6 +2321,17 @@ msgid "New Window" msgstr "새 ì°½" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "편집기 ì°½ì— ë³€í™”ê°€ ìžˆì„ ë•Œë§ˆë‹¤ íšŒì „í•©ë‹ˆë‹¤." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "ê°€ì ¸ì˜¨ 리소스를 ì €ìž¥í• ìˆ˜ 없습니다." @@ -2448,9 +2473,9 @@ msgid "" "Please read the documentation relevant to importing scenes to better " "understand this workflow." msgstr "" -"ì´ ë¦¬ì†ŒìŠ¤ëŠ” ê°€ì ¸ì˜¨ ì”¬ì— ì†í•œ 리소스ì´ë¯€ë¡œ íŽ¸ì§‘í• ìˆ˜ 없습니다.\n" -"ì´ ì›Œí¬í”Œë¡œë¥¼ ì´í•´í•˜ë ¤ë©´ 씬 ê°€ì ¸ì˜¤ê¸°(Importing Scenes)와 ê´€ë ¨ëœ ë¬¸ì„œë¥¼ ì½ì–´ì£¼" -"세요." +"`ì´ ë¦¬ì†ŒìŠ¤ëŠ” ê°€ì ¸ì˜¨ ì”¬ì— ì†í•œ 리소스ì´ë¯€ë¡œ íŽ¸ì§‘í• ìˆ˜ 없습니다.\n" +"ì´ ì›Œí¬í”Œë¡œë¥¼ ì´í•´í•˜ë ¤ë©´ 씬 ê°€ì ¸ì˜¤ê¸°(Importing Scenes)와 ê´€ë ¨ëœ ì„¤ëª…ë¬¸ì„œë¥¼ ì½" +"어주세요." #: editor/editor_node.cpp msgid "" @@ -2477,8 +2502,8 @@ msgid "" msgstr "" "ì´ ì”¬ì€ ê°€ì ¸ì˜¨ 것ì´ë¯€ë¡œ 변경 사í•ì´ ìœ ì§€ë˜ì§€ 않습니다.\n" "ì´ ì”¬ì„ ì¸ìŠ¤í„´ìŠ¤í™”í•˜ê±°ë‚˜ ìƒì†í•˜ë©´ íŽ¸ì§‘í• ìˆ˜ 있습니다.\n" -"ì´ ì›Œí¬í”Œë¡œë¥¼ ì´í•´í•˜ë ¤ë©´ 씬 ê°€ì ¸ì˜¤ê¸°(Importing Scenes)와 ê´€ë ¨ëœ ë¬¸ì„œë¥¼ ì½ì–´ì£¼" -"세요." +"ì´ ì›Œí¬í”Œë¡œë¥¼ ì´í•´í•˜ë ¤ë©´ 씬 ê°€ì ¸ì˜¤ê¸°(Importing Scenes)와 ê´€ë ¨ëœ ì„¤ëª…ë¬¸ì„œë¥¼ ì½" +"어주세요." #: editor/editor_node.cpp msgid "" @@ -2487,7 +2512,7 @@ msgid "" "this workflow." msgstr "" "ì›ê²© ê°ì²´ëŠ” 변경사í•ì´ ì ìš©ë˜ì§€ 않습니다.\n" -"ì´ ì›Œí¬í”Œë¡œë¥¼ ì´í•´í•˜ë ¤ë©´ 디버깅(Debugging)ê³¼ ê´€ë ¨ëœ ë¬¸ì„œë¥¼ ì½ì–´ì£¼ì„¸ìš”." +"ì´ ì›Œí¬í”Œë¡œë¥¼ ì´í•´í•˜ë ¤ë©´ 디버깅(Debugging)ê³¼ ê´€ë ¨ëœ ì„¤ëª…ë¬¸ì„œë¥¼ ì½ì–´ì£¼ì„¸ìš”." #: editor/editor_node.cpp msgid "There is no defined scene to run." @@ -2568,7 +2593,7 @@ msgstr "ì´ ìž‘ì—…ì—는 ì„ íƒí•œ 노드가 필요합니다." #: editor/editor_node.cpp msgid "Current scene not saved. Open anyway?" -msgstr "현재 ì”¬ì„ ì €ìž¥í•˜ì§€ 않았습니다. ë¬´ì‹œí•˜ê³ ì—´ê¹Œìš”?" +msgstr "현재 ì”¬ì´ ì €ìž¥ë˜ì–´ 있지 않습니다. ë¬´ì‹œí•˜ê³ ì—¬ì‹œê² ìŠµë‹ˆê¹Œ?" #: editor/editor_node.cpp msgid "Can't reload a scene that was never saved." @@ -2600,11 +2625,11 @@ msgstr "예" #: editor/editor_node.cpp msgid "Exit the editor?" -msgstr "편집기를 ëŒê¹Œìš”?" +msgstr "편집기를 ë‚˜ê°€ì‹œê² ìŠµë‹ˆê¹Œ?" #: editor/editor_node.cpp msgid "Open Project Manager?" -msgstr "프로ì 트 ë§¤ë‹ˆì €ë¥¼ 열까요?" +msgstr "프로ì 트 ë§¤ë‹ˆì €ë¥¼ ì—¬ì‹œê² ìŠµë‹ˆê¹Œ?" #: editor/editor_node.cpp msgid "Save & Quit" @@ -2612,11 +2637,11 @@ msgstr "ì €ìž¥ & 종료" #: editor/editor_node.cpp msgid "Save changes to the following scene(s) before quitting?" -msgstr "ë„기 ì „ì— í•´ë‹¹ ì”¬ì˜ ë³€ê²½ 사í•ì„ ì €ìž¥í• ê¹Œìš”?" +msgstr "종료하기 ì „ì— í•´ë‹¹ ì”¬ì˜ ë³€ê²½ 사í•ì„ ì €ìž¥í•˜ì‹œê² ìŠµë‹ˆê¹Œ?" #: editor/editor_node.cpp msgid "Save changes to the following scene(s) before opening Project Manager?" -msgstr "프로ì 트 ë§¤ë‹ˆì €ë¥¼ 열기 ì „ì— í•´ë‹¹ ì”¬ì˜ ë³€ê²½ 사í•ì„ ì €ìž¥í• ê¹Œìš”?" +msgstr "프로ì 트 매니터를 열기 ì „ì— í•´ë‹¹ ì”¬ì˜ ë³€ê²½ 사í•ì„ ì €ìž¥í•˜ì‹œê² ìŠµë‹ˆê¹Œ?" #: editor/editor_node.cpp msgid "" @@ -2949,7 +2974,7 @@ msgid "" msgstr "" "ì´ ì˜µì…˜ì´ í™œì„±í™” ëœ ê²½ìš° ì› í´ë¦ ë°°í¬ë¥¼ 사용하면 ì‹¤í–‰ì¤‘ì¸ í”„ë¡œì 트를 디버깅 " "í• ìˆ˜ 있ë„ë¡ì´ ì»´í“¨í„°ì˜ IPì— ì—°ê²°ì„ ì‹œë„합니다.\n" -"ì´ ì˜µì…˜ì€ ì›ê²© 디버깅 (ì¼ë°˜ì 으로 ëª¨ë°”ì¼ ìž¥ì¹˜ 사용)ì— ì‚¬ìš©í•˜ê¸°ìœ„í•œ 것입니" +"ì´ ì˜µì…˜ì€ ì›ê²© 디버깅 (ì¼ë°˜ì 으로 ëª¨ë°”ì¼ ê¸°ê¸° 사용)ì— ì‚¬ìš©í•˜ê¸° 위한 것입니" "다.\n" "GDScript 디버거를 로컬ì—서 사용하기 위해 활성화 í• í•„ìš”ëŠ” 없습니다." @@ -3009,8 +3034,8 @@ msgid "" msgstr "" "ì´ ì„¤ì •ì´ í™œì„±í™”ëœ ê²½ìš°, 편집기ì—서 ì”¬ì„ ìˆ˜ì •í•˜ë©´ ì‹¤í–‰ì¤‘ì¸ í”„ë¡œì íŠ¸ì— ë°˜ì˜ë©" "니다.\n" -"ì›ê²©ìž¥ì¹˜ì—서 ì‚¬ìš©ì¤‘ì¸ ê²½ìš° ë„¤íŠ¸ì›Œí¬ íŒŒì¼ ì‹œìŠ¤í…œ ê¸°ëŠ¥ì„ í™œì„±í™”í•˜ë©´ ë”ìš± 효율ì " -"입니다." +"기기ì—서 ì›ê²©ìœ¼ë¡œ ì‚¬ìš©ì¤‘ì¸ ê²½ìš° ë„¤íŠ¸ì›Œí¬ íŒŒì¼ ì‹œìŠ¤í…œ ê¸°ëŠ¥ì„ í™œì„±í™”í•˜ë©´ ë”ìš± " +"효율ì 입니다." #: editor/editor_node.cpp msgid "Synchronize Script Changes" @@ -3081,9 +3106,8 @@ msgid "Help" msgstr "ë„움ë§" #: editor/editor_node.cpp -#, fuzzy msgid "Online Documentation" -msgstr "문서 열기" +msgstr "온ë¼ì¸ 설명문서" #: editor/editor_node.cpp msgid "Questions & Answers" @@ -3161,10 +3185,6 @@ msgid "Save & Restart" msgstr "ì €ìž¥ & 다시 시작" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "편집기 ì°½ì— ë³€í™”ê°€ ìžˆì„ ë•Œë§ˆë‹¤ íšŒì „í•©ë‹ˆë‹¤." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "ìƒì‹œ ì—…ë°ì´íЏ" @@ -3210,9 +3230,8 @@ msgid "Install from file" msgstr "파ì¼ì—서 설치" #: editor/editor_node.cpp -#, fuzzy msgid "Select android sources file" -msgstr "소스 메시를 ì„ íƒí•˜ì„¸ìš”:" +msgstr "Android 소스 íŒŒì¼ ì„ íƒ" #: editor/editor_node.cpp msgid "" @@ -3358,9 +3377,8 @@ msgid "Update" msgstr "ì—…ë°ì´íЏ" #: editor/editor_plugin_settings.cpp -#, fuzzy msgid "Version" -msgstr "ë²„ì „:" +msgstr "ë²„ì „" #: editor/editor_plugin_settings.cpp #, fuzzy @@ -3609,7 +3627,7 @@ msgstr "" #: editor/export_template_manager.cpp msgid "Uninstall these templates." -msgstr "" +msgstr "ì´ í…œí”Œë¦¿ì„ ì œê±°í•©ë‹ˆë‹¤." #: editor/export_template_manager.cpp #, fuzzy @@ -3635,42 +3653,37 @@ msgid "Connecting to the mirror..." msgstr "ë¯¸ëŸ¬ì— ì—°ê²° 중..." #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't resolve the requested address." -msgstr "호스트 ì´ë¦„ì„ ì°¾ì„ ìˆ˜ ì—†ìŒ:" +msgstr "ìš”ì²ëœ 주소를 í•´ê²°í• ìˆ˜ 없습니다." #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't connect to the mirror." -msgstr "í˜¸ìŠ¤íŠ¸ì— ì—°ê²°í• ìˆ˜ ì—†ìŒ:" +msgstr "ë¯¸ëŸ¬ì— ì—°ê²°í• ìˆ˜ 없습니다." #: editor/export_template_manager.cpp -#, fuzzy msgid "No response from the mirror." -msgstr "í˜¸ìŠ¤íŠ¸ì˜ ì‘답 ì—†ìŒ:" +msgstr "미러로부터 ì‘ë‹´ì´ ì—†ìŠµë‹ˆë‹¤." #: editor/export_template_manager.cpp #: editor/plugins/asset_library_editor_plugin.cpp msgid "Request failed." -msgstr "ìš”ì² ì‹¤íŒ¨í•¨." +msgstr "ìš”ì²ì— 실패했습니다." #: editor/export_template_manager.cpp -#, fuzzy msgid "Request ended up in a redirect loop." -msgstr "ìš”ì² ì‹¤íŒ¨. 너무 ë§Žì€ ë¦¬ë‹¤ì´ë ‰íЏ" +msgstr "ìš”ì²ì´ ë¦¬ë””ë ‰ì…˜ 루프로 ë났습니다." #: editor/export_template_manager.cpp -#, fuzzy msgid "Request failed:" -msgstr "ìš”ì² ì‹¤íŒ¨í•¨." +msgstr "ìš”ì² ì‹¤íŒ¨ë¨:" #: editor/export_template_manager.cpp msgid "Download complete; extracting templates..." -msgstr "" +msgstr "다운로드를 완료하여 í…œí”Œë¦¿ì„ ì••ì¶• í•´ì œ 중..." #: editor/export_template_manager.cpp msgid "Cannot remove temporary file:" -msgstr "임시 파ì¼ì„ ì €ìž¥í• ìˆ˜ ì—†ìŒ:" +msgstr "임시 파ì¼ì„ ì‚ì œí• ìˆ˜ ì—†ìŒ:" #: editor/export_template_manager.cpp msgid "" @@ -3691,7 +3704,7 @@ msgstr "미러 목ë¡ì˜ JSON 구문 ë¶„ì„ ì¤‘ 오류. ì´ ë¬¸ì œë¥¼ ì‹ ê³ í•´ì #: editor/export_template_manager.cpp msgid "Best available mirror" -msgstr "" +msgstr "최ìƒì˜ 사용 가능한 미러" #: editor/export_template_manager.cpp msgid "" @@ -3807,17 +3820,26 @@ msgstr "" #: editor/export_template_manager.cpp msgid "Uninstall" -msgstr "ì‚ì œ" +msgstr "ì œê±°" #: editor/export_template_manager.cpp -#, fuzzy msgid "Uninstall templates for the current version." -msgstr "ì¹´ìš´í„°ì˜ ì´ˆê¸° ê°’" +msgstr "현재 ë²„ì „ì„ ìœ„í•œ í…œí”Œë¦¿ì„ ì œê±°í•©ë‹ˆë‹¤." #: editor/export_template_manager.cpp #, fuzzy msgid "Download from:" -msgstr "다운로드 오류" +msgstr "ë‹¤ìŒ ìœ„ì¹˜ì—서 다운로드:" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "브ë¼ìš°ì €ì—서 실행" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "복사 오류" #: editor/export_template_manager.cpp msgid "Download and Install" @@ -3839,9 +3861,8 @@ msgid "Install from File" msgstr "파ì¼ì—서 설치" #: editor/export_template_manager.cpp -#, fuzzy msgid "Install templates from a local file." -msgstr "ZIP 파ì¼ì—서 템플릿 ê°€ì ¸ì˜¤ê¸°" +msgstr "로컬 파ì¼ë¡œë¶€í„° í…œí”Œë¦¿ì„ ì„¤ì¹˜í•©ë‹ˆë‹¤." #: editor/export_template_manager.cpp editor/find_in_files.cpp #: editor/progress_dialog.cpp scene/gui/dialogs.cpp @@ -3859,9 +3880,8 @@ msgid "Other Installed Versions:" msgstr "ì„¤ì¹˜ëœ ë²„ì „:" #: editor/export_template_manager.cpp -#, fuzzy msgid "Uninstall Template" -msgstr "ì‚ì œ" +msgstr "템플릿 ì œê±°" #: editor/export_template_manager.cpp msgid "Select Template File" @@ -4401,9 +4421,8 @@ msgid "Save As..." msgstr "다른 ì´ë¦„으로 ì €ìž¥..." #: editor/inspector_dock.cpp -#, fuzzy msgid "Extra resource options." -msgstr "리소스 ê²½ë¡œì— ì—†ìŠµë‹ˆë‹¤." +msgstr "별ë„ì˜ ë¦¬ì†ŒìŠ¤ 옵션." #: editor/inspector_dock.cpp #, fuzzy @@ -4432,13 +4451,12 @@ msgid "History of recently edited objects." msgstr "ìµœê·¼ì— íŽ¸ì§‘í•œ ê°ì²´ 기ë¡ìž…니다." #: editor/inspector_dock.cpp -#, fuzzy msgid "Open documentation for this object." -msgstr "문서 열기" +msgstr "ì´ ê°ì²´ë¥¼ 위한 설명문서를 엽니다." #: editor/inspector_dock.cpp editor/scene_tree_dock.cpp msgid "Open Documentation" -msgstr "문서 열기" +msgstr "설명문서 열기" #: editor/inspector_dock.cpp msgid "Filter properties" @@ -4690,9 +4708,8 @@ msgid "Blend:" msgstr "혼합:" #: editor/plugins/animation_blend_tree_editor_plugin.cpp -#, fuzzy msgid "Parameter Changed:" -msgstr "매개변수 변경ë¨" +msgstr "매개변수 변경ë¨:" #: editor/plugins/animation_blend_tree_editor_plugin.cpp #: editor/plugins/animation_tree_player_editor_plugin.cpp @@ -5526,7 +5543,7 @@ msgstr "미리 보기" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Configure Snap" -msgstr "스냅 ì„¤ì •" +msgstr "스냅 구성" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Grid Offset:" @@ -5802,27 +5819,22 @@ msgstr "모드 ì„ íƒ" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Drag: Rotate selected node around pivot." -msgstr "ì„ íƒí•œ 노드나 ì „í™˜ì„ ì‚ì œí•©ë‹ˆë‹¤." +msgstr "드래그: 피벗 ì£¼ìœ„ì— ì„ íƒëœ 노드를 íšŒì „í•©ë‹ˆë‹¤." #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Alt+Drag: Move selected node." -msgstr "Alt+드래그: ì´ë™" +msgstr "Alt+드래그: ì„ íƒëœ 노드를 ì´ë™í•©ë‹ˆë‹¤." #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "V: Set selected node's pivot position." -msgstr "ì„ íƒí•œ 노드나 ì „í™˜ì„ ì‚ì œí•©ë‹ˆë‹¤." +msgstr "V: ì„ íƒëœ ë…¸ë“œì˜ í”¼ë²— 위치를 ì„¤ì •í•©ë‹ˆë‹¤." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Alt+RMB: Show list of all nodes at position clicked, including locked." msgstr "" -"í´ë¦í•œ ìœ„ì¹˜ì— ìžˆëŠ” ëª¨ë“ ê°ì²´ 목ë¡ì„ 보여줘요\n" -"(ì„ íƒ ëª¨ë“œì—서 Alt+ìš°í´ë¦ê³¼ ê°™ìŒ)." +"Alt+ìš°í´ë¦: í´ë¦ëœ ìœ„ì¹˜ì— ìžˆëŠ” ìž ê¸ˆì„ í¬í•¨í•œ ëª¨ë“ ë…¸ë“œì˜ ëª©ë¡ì„ ë³´ì—¬ì¤ë‹ˆë‹¤." #: editor/plugins/canvas_item_editor_plugin.cpp msgid "RMB: Add node at position clicked." @@ -5831,12 +5843,12 @@ msgstr "" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp msgid "Move Mode" -msgstr "ì´ë™ëª¨ë“œ" +msgstr "ì´ë™ 모드" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp msgid "Rotate Mode" -msgstr "íšŒì „ëª¨ë“œ" +msgstr "íšŒì „ 모드" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp @@ -5866,7 +5878,7 @@ msgstr "ìž ëª¨ë“œ" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Toggle smart snapping." -msgstr "스마트 스냅 í† ê¸€." +msgstr "스마트 ìŠ¤ëƒ…ì„ í† ê¸€í•©ë‹ˆë‹¤." #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Use Smart Snap" @@ -5874,7 +5886,7 @@ msgstr "스마트 스냅 사용" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Toggle grid snapping." -msgstr "ê²©ìž ìŠ¤ëƒ… í† ê¸€." +msgstr "ê²©ìž ìŠ¤ëƒ…ì„ í† ê¸€í•©ë‹ˆë‹¤." #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Use Grid Snap" @@ -5907,7 +5919,7 @@ msgstr "스마트 스냅" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp msgid "Configure Snap..." -msgstr "스냅 ì„¤ì •..." +msgstr "스냅 구성..." #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Snap to Parent" @@ -6410,9 +6422,8 @@ msgid "No mesh to debug." msgstr "ë””ë²„ê·¸í• ë©”ì‹œê°€ 없습니다." #: editor/plugins/mesh_instance_editor_plugin.cpp -#, fuzzy msgid "Mesh has no UV in layer %d." -msgstr "ì´ ë ˆì´ì–´ì—서 모ë¸ì€ UVê°€ 없습니다" +msgstr "ë ˆì´ì–´ %dì—서 ë©”ì‹œì— UVê°€ 없습니다." #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "MeshInstance lacks a Mesh!" @@ -7068,7 +7079,7 @@ msgstr "ê²©ìž ë³´ì´ê¸°" #: editor/plugins/polygon_2d_editor_plugin.cpp msgid "Configure Grid:" -msgstr "ê²©ìž ì„¤ì •:" +msgstr "ê²©ìž êµ¬ì„±:" #: editor/plugins/polygon_2d_editor_plugin.cpp msgid "Grid Offset X:" @@ -7146,14 +7157,12 @@ msgid "Flip Portals" msgstr "수í‰ìœ¼ë¡œ 뒤집기" #: editor/plugins/room_manager_editor_plugin.cpp -#, fuzzy msgid "Room Generate Points" -msgstr "ìƒì„±í•œ ì 개수:" +msgstr "ë°© ìƒì„±í•œ ì 개수" #: editor/plugins/room_manager_editor_plugin.cpp -#, fuzzy msgid "Generate Points" -msgstr "ìƒì„±í•œ ì 개수:" +msgstr "ìƒì„±í•œ ì 개수" #: editor/plugins/room_manager_editor_plugin.cpp #, fuzzy @@ -7402,11 +7411,11 @@ msgstr "온ë¼ì¸ 문서" #: editor/plugins/script_editor_plugin.cpp msgid "Open Godot online documentation." -msgstr "Godot 온ë¼ì¸ 문서를 엽니다." +msgstr "Godot 온ë¼ì¸ 설명문서를 엽니다." #: editor/plugins/script_editor_plugin.cpp msgid "Search the reference documentation." -msgstr "참조 문서를 검색합니다." +msgstr "참조 설명문서를 검색합니다." #: editor/plugins/script_editor_plugin.cpp msgid "Go to previous edited document." @@ -7607,7 +7616,7 @@ msgstr "ì´ì „ ë¶ë§ˆí¬ë¡œ ì´ë™" #: editor/plugins/script_text_editor.cpp msgid "Remove All Bookmarks" -msgstr "ëª¨ë“ ë¶ë§ˆí¬ ì œê±°" +msgstr "ëª¨ë“ ë¶ë§ˆí¬ ì‚ì œ" #: editor/plugins/script_text_editor.cpp msgid "Go to Function..." @@ -7624,7 +7633,7 @@ msgstr "중단ì í† ê¸€" #: editor/plugins/script_text_editor.cpp msgid "Remove All Breakpoints" -msgstr "중단ì ëª¨ë‘ ì œê±°" +msgstr "ëª¨ë“ ì¤‘ë‹¨ì ì‚ì œ" #: editor/plugins/script_text_editor.cpp msgid "Go to Next Breakpoint" @@ -7721,20 +7730,17 @@ msgid "None" msgstr "ì—†ìŒ" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Rotate" -msgstr "주(State)" +msgstr "íšŒì „" #. TRANSLATORS: This refers to the movement that changes the position of an object. #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Translate" -msgstr "ì´ë™:" +msgstr "ì´ë™" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Scale" -msgstr "í¬ê¸°:" +msgstr "í¬ê¸°" #: editor/plugins/spatial_editor_plugin.cpp msgid "Scaling: " @@ -7757,48 +7763,40 @@ msgid "Animation Key Inserted." msgstr "ì• ë‹ˆë©”ì´ì…˜ 키를 삽입했습니다." #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Pitch:" -msgstr "피치" +msgstr "피치:" #: editor/plugins/spatial_editor_plugin.cpp msgid "Yaw:" msgstr "" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Size:" -msgstr "í¬ê¸°: " +msgstr "í¬ê¸°:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Objects Drawn:" -msgstr "ê·¸ë ¤ì§„ ê°ì²´" +msgstr "ê·¸ë ¤ì§„ ê°ì²´:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Material Changes:" -msgstr "머티리얼 바꾸기" +msgstr "머티리얼 바꾸기:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Shader Changes:" -msgstr "ì…°ì´ë” 바꾸기" +msgstr "ì…°ì´ë” 바꾸기:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Surface Changes:" -msgstr "표면 바꾸기" +msgstr "표면 바꾸기:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Draw Calls:" -msgstr "드로우 콜" +msgstr "드로우 콜:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Vertices:" -msgstr "ì " +msgstr "ì •ì :" #: editor/plugins/spatial_editor_plugin.cpp msgid "FPS: %d (%s ms)" @@ -8459,71 +8457,63 @@ msgstr "ìŠ¤íƒ€ì¼ ë°•ìŠ¤" #: editor/plugins/theme_editor_plugin.cpp msgid "{num} color(s)" -msgstr "" +msgstr "{num}색" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No colors found." -msgstr "하위 리소스를 ì°¾ì„ ìˆ˜ 없습니다." +msgstr "ìƒ‰ì„ ì°¾ì„ ìˆ˜ 없습니다." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "{num} constant(s)" -msgstr "ìƒìˆ˜" +msgstr "ìƒìˆ˜ {num}ê°œ" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No constants found." -msgstr "ìƒ‰ìƒ ìƒìˆ˜." +msgstr "ìƒìˆ˜ë¥¼ ì°¾ì„ ìˆ˜ 없습니다." #: editor/plugins/theme_editor_plugin.cpp msgid "{num} font(s)" -msgstr "" +msgstr "글꼴 {num}ê°œ" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No fonts found." -msgstr "ì°¾ì„ ìˆ˜ ì—†ìŒ!" +msgstr "ê¸€ê¼´ì„ ì°¾ì„ ìˆ˜ 없습니다." #: editor/plugins/theme_editor_plugin.cpp msgid "{num} icon(s)" -msgstr "" +msgstr "ì•„ì´ì½˜ {num}ê°œ" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No icons found." -msgstr "ì°¾ì„ ìˆ˜ ì—†ìŒ!" +msgstr "ì•„ì´ì½˜ì„ ì°¾ì„ ìˆ˜ 없습니다." #: editor/plugins/theme_editor_plugin.cpp msgid "{num} stylebox(es)" -msgstr "" +msgstr "스타ì¼ë°•스 {num}ê°œ" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No styleboxes found." -msgstr "하위 리소스를 ì°¾ì„ ìˆ˜ 없습니다." +msgstr "스타ì¼ë°•스를 ì°¾ì„ ìˆ˜ 없습니다." #: editor/plugins/theme_editor_plugin.cpp msgid "{num} currently selected" -msgstr "" +msgstr "현재 ì„ íƒ {num}ê°œ" #: editor/plugins/theme_editor_plugin.cpp msgid "Nothing was selected for the import." -msgstr "" +msgstr "ê°€ì ¸ì˜¬ ê²ƒì´ ì„ íƒë˜ì§€ 않았습니다." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Importing Theme Items" -msgstr "테마 ê°€ì ¸ì˜¤ê¸°" +msgstr "테마 í•ëª©ì„ ê°€ì ¸ì˜¤ëŠ” 중" #: editor/plugins/theme_editor_plugin.cpp msgid "Importing items {n}/{n}" -msgstr "" +msgstr "í•목 {n}/{n} ê°€ì ¸ì˜¤ëŠ” 중" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Updating the editor" -msgstr "편집기를 ëŒê¹Œìš”?" +msgstr "편집기를 ì—…ë°ì´íЏ 중" #: editor/plugins/theme_editor_plugin.cpp #, fuzzy @@ -8531,18 +8521,16 @@ msgid "Finalizing" msgstr "ë¶„ì„ ì¤‘" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Filter:" -msgstr "í•„í„°: " +msgstr "í•„í„°:" #: editor/plugins/theme_editor_plugin.cpp msgid "With Data" -msgstr "" +msgstr "ë°ì´í„°ì™€ 함께" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select by data type:" -msgstr "노드를 ì„ íƒí•˜ì„¸ìš”" +msgstr "ë°ì´í„° ìœ í˜• 별 ì„ íƒ:" #: editor/plugins/theme_editor_plugin.cpp #, fuzzy @@ -8558,9 +8546,8 @@ msgid "Deselect all visible color items." msgstr "" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible constant items." -msgstr "ë¨¼ì € ì„¤ì • í•ëª©ì„ ì„ íƒí•˜ì„¸ìš”!" +msgstr "ë³´ì´ëŠ” ëª¨ë“ ìƒìˆ˜ í•ëª©ì„ ì„ íƒí•©ë‹ˆë‹¤." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible constant items and their data." @@ -8571,9 +8558,8 @@ msgid "Deselect all visible constant items." msgstr "" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible font items." -msgstr "ë¨¼ì € ì„¤ì • í•ëª©ì„ ì„ íƒí•˜ì„¸ìš”!" +msgstr "ë³´ì´ëŠ” ëª¨ë“ ê¸€ê¼´ í•ëª©ì„ ì„ íƒí•©ë‹ˆë‹¤." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible font items and their data." @@ -8584,19 +8570,16 @@ msgid "Deselect all visible font items." msgstr "" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible icon items." -msgstr "ë¨¼ì € ì„¤ì • í•ëª©ì„ ì„ íƒí•˜ì„¸ìš”!" +msgstr "ë³´ì´ëŠ” ëª¨ë“ ì•„ì´ì½˜ í•ëª©ì„ ì„ íƒí•©ë‹ˆë‹¤." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible icon items and their data." -msgstr "ë¨¼ì € ì„¤ì • í•ëª©ì„ ì„ íƒí•˜ì„¸ìš”!" +msgstr "ë³´ì´ëŠ” ëª¨ë“ ì•„ì´ì½˜ í•목과 ê·¸ í•ëª©ì˜ ë°ì´í„°ë¥¼ ì„ íƒí•©ë‹ˆë‹¤." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Deselect all visible icon items." -msgstr "ë¨¼ì € ì„¤ì • í•ëª©ì„ ì„ íƒí•˜ì„¸ìš”!" +msgstr "ë³´ì´ëŠ” ëª¨ë“ ì•„ì´ì½˜ í•ëª©ì„ ì„ íƒ í•´ì œí•©ë‹ˆë‹¤." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible stylebox items." @@ -8617,19 +8600,16 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Collapse types." -msgstr "ëª¨ë‘ ì ‘ê¸°" +msgstr "ìœ í˜•ì„ ì ‘ìŠµë‹ˆë‹¤." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Expand types." -msgstr "ëª¨ë‘ íŽ¼ì¹˜ê¸°" +msgstr "ìœ í˜•ì„ íŽ¼ì¹©ë‹ˆë‹¤." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all Theme items." -msgstr "템플릿 íŒŒì¼ ì„ íƒ" +msgstr "ëª¨ë“ í…Œë§ˆ í•ëª©ì„ ì„ íƒí•©ë‹ˆë‹¤." #: editor/plugins/theme_editor_plugin.cpp #, fuzzy @@ -8662,6 +8642,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "ëª¨ë“ í•목 ì‚ì œ" @@ -8692,6 +8678,12 @@ msgid "Remove All StyleBox Items" msgstr "ëª¨ë“ í•목 ì‚ì œ" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "í´ëž˜ìФ í•목 추가" @@ -8771,9 +8763,8 @@ msgid "Add Type:" msgstr "ìœ í˜•:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Item:" -msgstr "í•목 추가" +msgstr "í•목 추가:" #: editor/plugins/theme_editor_plugin.cpp #, fuzzy @@ -8781,9 +8772,8 @@ msgid "Add StyleBox Item" msgstr "ëª¨ë“ í•목 추가" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove Items:" -msgstr "í•목 ì‚ì œ" +msgstr "í•목 ì‚ì œ:" #: editor/plugins/theme_editor_plugin.cpp msgid "Remove Class Items" @@ -8824,9 +8814,8 @@ msgid "Editor Theme" msgstr "테마 편집" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select Another Theme Resource:" -msgstr "리소스 ì‚ì œ" +msgstr "다른 테마 리소스 ì„ íƒ:" #: editor/plugins/theme_editor_plugin.cpp #, fuzzy @@ -8869,9 +8858,8 @@ msgid "Add Item Type" msgstr "í•목 추가" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Node Types:" -msgstr "노드 ìœ í˜•" +msgstr "노드 ìœ í˜•:" #: editor/plugins/theme_editor_plugin.cpp #, fuzzy @@ -8892,9 +8880,8 @@ msgid "Override all default type items." msgstr "" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Theme:" -msgstr "테마" +msgstr "테마:" #: editor/plugins/theme_editor_plugin.cpp #, fuzzy @@ -8959,9 +8946,8 @@ msgid "Checked Radio Item" msgstr "ì²´í¬ëœ ë¼ë””오 í•목" #: editor/plugins/theme_editor_preview.cpp -#, fuzzy msgid "Named Separator" -msgstr "ì´ë¦„있는 구분ìž." +msgstr "ì´ë¦„ 있는 구분ìž" #: editor/plugins/theme_editor_preview.cpp msgid "Submenu" @@ -9135,7 +9121,7 @@ msgstr "TileSetì— í…스처를 추가합니다." #: editor/plugins/tile_set_editor_plugin.cpp msgid "Remove selected Texture from TileSet." -msgstr "ì„ íƒí•œ í…스처를 TileSetì—서 ì œê±°í•©ë‹ˆë‹¤." +msgstr "ì„ íƒëœ í…스처를 TileSetì—서 ì‚ì œí•©ë‹ˆë‹¤." #: editor/plugins/tile_set_editor_plugin.cpp msgid "Create from Scene" @@ -10399,9 +10385,8 @@ msgid "VisualShader" msgstr "비주얼 ì…°ì´ë”" #: editor/plugins/visual_shader_editor_plugin.cpp -#, fuzzy msgid "Edit Visual Property:" -msgstr "비주얼 ì†ì„± 편집" +msgstr "비주얼 ì†ì„± 편집:" #: editor/plugins/visual_shader_editor_plugin.cpp msgid "Visual Shader Mode Changed" @@ -10844,14 +10829,12 @@ msgid "Are you sure to run %d projects at once?" msgstr "한 ë²ˆì— %dê°œì˜ í”„ë¡œì 트를 ì‹¤í–‰í• ê±´ê°€ìš”?" #: editor/project_manager.cpp -#, fuzzy msgid "Remove %d projects from the list?" -msgstr "목ë¡ì—서 기기 ì„ íƒ" +msgstr "목ë¡ì—서 프로ì 트 %d개를 ì‚ì œí•˜ì‹œê² ìŠµë‹ˆê¹Œ?" #: editor/project_manager.cpp -#, fuzzy msgid "Remove this project from the list?" -msgstr "목ë¡ì—서 기기 ì„ íƒ" +msgstr "목ë¡ì—서 ì´ í”„ë¡œì 트를 ì‚ì œí•˜ì‹œê² ìŠµë‹ˆê¹Œ?" #: editor/project_manager.cpp msgid "" @@ -12312,6 +12295,16 @@ msgstr "ìº¡ìŠ ëª¨ì–‘ ë†’ì´ ë°”ê¾¸ê¸°" msgid "Change Ray Shape Length" msgstr "ê´‘ì„ ëª¨ì–‘ ê¸¸ì´ ë°”ê¾¸ê¸°" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "ê³¡ì„ ì 위치 ì„¤ì •" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "ê³¡ì„ ì 위치 ì„¤ì •" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "ì›ê¸°ë‘¥ 반지름 바꾸기" @@ -13109,17 +13102,15 @@ msgstr "목ë¡ì—서 기기 ì„ íƒ" #: platform/android/export/export.cpp msgid "Running on %s" -msgstr "" +msgstr "%sì—서 실행" #: platform/android/export/export.cpp -#, fuzzy msgid "Exporting APK..." -msgstr "ëª¨ë‘ ë‚´ë³´ë‚´ê¸°" +msgstr "APK로 내보내는 중..." #: platform/android/export/export.cpp -#, fuzzy msgid "Uninstalling..." -msgstr "ì‚ì œ" +msgstr "ì œê±° 중..." #: platform/android/export/export.cpp #, fuzzy @@ -13127,14 +13118,12 @@ msgid "Installing to device, please wait..." msgstr "로드 중, ê¸°ë‹¤ë ¤ 주세요..." #: platform/android/export/export.cpp -#, fuzzy msgid "Could not install to device: %s" -msgstr "ì”¬ì„ ì¸ìŠ¤í„´ìŠ¤ í• ìˆ˜ 없습니다!" +msgstr "ê¸°ê¸°ì— ì„¤ì¹˜í• ìˆ˜ ì—†ìŒ: %s" #: platform/android/export/export.cpp -#, fuzzy msgid "Running on device..." -msgstr "맞춤 스í¬ë¦½íЏ 실행 중..." +msgstr "기기ì—서 실행 중..." #: platform/android/export/export.cpp #, fuzzy @@ -13163,7 +13152,7 @@ msgstr "" #: platform/android/export/export.cpp msgid "Debug keystore not configured in the Editor Settings nor in the preset." -msgstr "Debug keystore를 편집기 ì„¤ì •ê³¼ í”„ë¦¬ì…‹ì— ì„¤ì •í•˜ì§€ 않았습니다." +msgstr "Debug keystore를 편집기 ì„¤ì •ê³¼ í”„ë¦¬ì…‹ì— êµ¬ì„±í•˜ì§€ 않았습니다." #: platform/android/export/export.cpp msgid "" @@ -13175,7 +13164,7 @@ msgstr "" #: platform/android/export/export.cpp msgid "Release keystore incorrectly configured in the export preset." -msgstr "내보내기 í”„ë¦¬ì…‹ì— ë°°í¬ keystorkeê°€ 잘못 ì„¤ì •ë˜ì–´ 있습니다." +msgstr "내보내기 í”„ë¦¬ì…‹ì— ì¶œì‹œ keystorkeê°€ 잘못 구성ë˜ì–´ 있습니다." #: platform/android/export/export.cpp msgid "A valid Android SDK path is required in Editor Settings." @@ -13187,7 +13176,7 @@ msgstr "편집기 ì„¤ì •ì—서 ìž˜ëª»ëœ Android SDK 경로입니다." #: platform/android/export/export.cpp msgid "Missing 'platform-tools' directory!" -msgstr "'platform-tools' ë””ë ‰í„°ë¦¬ê°€ 없습니다!" +msgstr "'platform-tools' ë””ë ‰í† ë¦¬ê°€ 없습니다!" #: platform/android/export/export.cpp msgid "Unable to find Android SDK platform-tools' adb command." @@ -13195,11 +13184,11 @@ msgstr "Android SDK platform-toolsì˜ adb ëª…ë ¹ì„ ì°¾ì„ ìˆ˜ 없습니다." #: platform/android/export/export.cpp msgid "Please check in the Android SDK directory specified in Editor Settings." -msgstr "편집기 ì„¤ì •ì—서 ì§€ì •ëœ Android SDK ë””ë ‰í„°ë¦¬ë¥¼ 확ì¸í•´ì£¼ì„¸ìš”." +msgstr "편집기 ì„¤ì •ì—서 ì§€ì •ëœ Android SDK ë””ë ‰í† ë¦¬ë¥¼ 확ì¸í•´ì£¼ì„¸ìš”." #: platform/android/export/export.cpp msgid "Missing 'build-tools' directory!" -msgstr "'build-tools' ë””ë ‰í„°ë¦¬ê°€ 없습니다!" +msgstr "'build-tools' ë””ë ‰í† ë¦¬ê°€ 없습니다!" #: platform/android/export/export.cpp msgid "Unable to find Android SDK build-tools' apksigner command." @@ -13263,16 +13252,12 @@ msgid "Signing debug %s..." msgstr "" #: platform/android/export/export.cpp -#, fuzzy msgid "Signing release %s..." -msgstr "" -"íŒŒì¼ ìŠ¤ìº”ì¤‘.\n" -"ê¸°ë‹¤ë ¤ì£¼ì‹ì‹œì˜¤..." +msgstr "출시 %sì— ì„œëª… 중..." #: platform/android/export/export.cpp -#, fuzzy msgid "Could not find keystore, unable to export." -msgstr "내보내기 í…œí”Œë¦¿ì„ ì—´ 수 ì—†ìŒ:" +msgstr "keystore를 ì°¾ì„ ìˆ˜ 없어, 내보낼 수 없었습니다." #: platform/android/export/export.cpp msgid "'apksigner' returned with error #%d" @@ -13288,9 +13273,8 @@ msgid "'apksigner' verification of %s failed." msgstr "" #: platform/android/export/export.cpp -#, fuzzy msgid "Exporting for Android" -msgstr "ëª¨ë‘ ë‚´ë³´ë‚´ê¸°" +msgstr "Android용으로 내보내는 중" #: platform/android/export/export.cpp msgid "Invalid filename! Android App Bundle requires the *.aab extension." @@ -13334,14 +13318,12 @@ msgid "" msgstr "" #: platform/android/export/export.cpp -#, fuzzy msgid "Could not export project files to gradle project\n" -msgstr "프로ì 트 ê²½ë¡œì— project.godot 파ì¼ì„ ì°¾ì„ ìˆ˜ 없습니다." +msgstr "프로ì 트 파ì¼ì„ gradle 프로ì 트로 내보낼 수 없었습니다\n" #: platform/android/export/export.cpp -#, fuzzy msgid "Could not write expansion package file!" -msgstr "파ì¼ì— 쓸 수 ì—†ìŒ:" +msgstr "확장 패키지 파ì¼ì„ 쓸 수 없었습니다!" #: platform/android/export/export.cpp msgid "Building Android Project (gradle)" @@ -13353,7 +13335,7 @@ msgid "" "Alternatively visit docs.godotengine.org for Android build documentation." msgstr "" "Android 프로ì íŠ¸ì˜ ë¹Œë“œì— ì‹¤íŒ¨í–ˆìŠµë‹ˆë‹¤, ì¶œë ¥ëœ ì˜¤ë¥˜ë¥¼ 확ì¸í•˜ì„¸ìš”.\n" -"ë˜ëŠ” docs.godotengine.orgì—서 Andoid 빌드 문서를 찾아보세요." +"ë˜ëŠ” docs.godotengine.orgì—서 Android 빌드 설명문서를 찾아보세요." #: platform/android/export/export.cpp msgid "Moving output" @@ -13365,7 +13347,7 @@ msgid "" "outputs." msgstr "" "내보내기 파ì¼ì„ ë³µì‚¬í•˜ê³ ì´ë¦„ì„ ë°”ê¿€ 수 없습니다, ì¶œë ¥ì— ëŒ€í•œ gradle 프로ì " -"트 ë””ë ‰í„°ë¦¬ë¥¼ 확ì¸í•˜ì„¸ìš”." +"트 ë””ë ‰í† ë¦¬ë¥¼ 확ì¸í•˜ì„¸ìš”." #: platform/android/export/export.cpp #, fuzzy @@ -13373,16 +13355,16 @@ msgid "Package not found: %s" msgstr "ì• ë‹ˆë©”ì´ì…˜ì„ ì°¾ì„ ìˆ˜ ì—†ìŒ: '%s'" #: platform/android/export/export.cpp -#, fuzzy msgid "Creating APK..." -msgstr "ìœ¤ê³½ì„ ë§Œë“œëŠ” 중..." +msgstr "APK를 만드는 중..." #: platform/android/export/export.cpp -#, fuzzy msgid "" "Could not find template APK to export:\n" "%s" -msgstr "내보내기 í…œí”Œë¦¿ì„ ì—´ 수 ì—†ìŒ:" +msgstr "" +"내보낼 템플릿 APK를 ì°¾ì„ ìˆ˜ ì—†ìŒ:\n" +"%s" #: platform/android/export/export.cpp msgid "" @@ -13393,14 +13375,12 @@ msgid "" msgstr "" #: platform/android/export/export.cpp -#, fuzzy msgid "Adding files..." -msgstr "%s 추가하는 중..." +msgstr "파ì¼ì„ 추가하는 중..." #: platform/android/export/export.cpp -#, fuzzy msgid "Could not export project files" -msgstr "파ì¼ì— 쓸 수 ì—†ìŒ:" +msgstr "프로ì 트 파ì¼ì„ 내보낼 수 없었습니다" #: platform/android/export/export.cpp msgid "Aligning APK..." @@ -13455,29 +13435,24 @@ msgid "Could not write file:" msgstr "파ì¼ì— 쓸 수 ì—†ìŒ:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Could not read file:" -msgstr "파ì¼ì— 쓸 수 ì—†ìŒ:" +msgstr "파ì¼ì„ ì½ì„ 수 ì—†ìŒ:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Could not read HTML shell:" -msgstr "맞춤 HTML shellì„ ì½ì„ 수 ì—†ìŒ:" +msgstr "HTML shellì„ ì½ì„ 수 ì—†ìŒ:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Could not create HTTP server directory:" -msgstr "í´ë”를 만들 수 없습니다." +msgstr "HTTP 서버 ë””ë ‰í† ë¦¬ë¥¼ 만들 수 ì—†ìŒ:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Error starting HTTP server:" -msgstr "씬 ì €ìž¥ 중 오류." +msgstr "HTTP 서버를 시작하는 중 오류:" #: platform/osx/export/export.cpp -#, fuzzy msgid "Invalid bundle identifier:" -msgstr "ìž˜ëª»ëœ ì‹ë³„ìž:" +msgstr "ìž˜ëª»ëœ bundle ì‹ë³„ìž:" #: platform/osx/export/export.cpp msgid "Notarization: code signing required." @@ -14052,6 +14027,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "ì´ ë°”ë””ëŠ” 메시를 ì„¤ì •í• ë•Œê¹Œì§€ 무시ë©ë‹ˆë‹¤." @@ -14158,7 +14169,7 @@ msgid "" msgstr "" "색ìƒ: #%s\n" "좌í´ë¦: ìƒ‰ìƒ ì„¤ì •\n" -"ìš°í´ë¦: 프리셋 ì œê±°" +"ìš°í´ë¦: 프리셋 ì‚ì œ" #: scene/gui/color_picker.cpp msgid "Pick a color from the editor window." @@ -15474,9 +15485,6 @@ msgstr "ìƒìˆ˜ëŠ” ìˆ˜ì •í• ìˆ˜ 없습니다." #~ msgid "I see..." #~ msgstr "ì•Œê² ìŠµë‹ˆë‹¤..." -#~ msgid "Can't open '%s'." -#~ msgstr "'%s' 열수 ì—†ìŒ." - #~ msgid "Ugh" #~ msgstr "오우" diff --git a/editor/translations/lt.po b/editor/translations/lt.po index ab98c9b156..f8bc356023 100644 --- a/editor/translations/lt.po +++ b/editor/translations/lt.po @@ -349,6 +349,7 @@ msgstr "Keiskite animacijos ciklo režimÄ…" msgid "Remove Anim Track" msgstr "Animacija: panaikinti įrašą" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "" @@ -373,10 +374,26 @@ msgstr "Sukurti" msgid "Anim Insert" msgstr "Animacija: įterpti" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animacija" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "property '%s'" +msgstr "" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "" @@ -962,7 +979,7 @@ msgstr "Sukurti NaujÄ…" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2288,6 +2305,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -3079,10 +3107,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "" @@ -3710,6 +3734,15 @@ msgid "Download from:" msgstr "Atsisiuntimo Klaida" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Atidaryti" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8555,6 +8588,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "MÄ—gstamiausi:" @@ -8583,6 +8622,12 @@ msgid "Remove All StyleBox Items" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "MÄ—gstamiausi:" @@ -12121,6 +12166,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13758,6 +13811,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/lv.po b/editor/translations/lv.po index 3c6ab5cb19..180cd1be1c 100644 --- a/editor/translations/lv.po +++ b/editor/translations/lv.po @@ -344,6 +344,7 @@ msgstr "IzmainÄ«t AnimÄcijas AtkÄrtoÅ¡anÄs Režīmu" msgid "Remove Anim Track" msgstr "Noņemt Anim. Celiņu" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Izveidot JAUNU celiņu priekÅ¡ %s un ievietot atslÄ“gievietni?" @@ -368,10 +369,26 @@ msgstr "Izveidot" msgid "Anim Insert" msgstr "Anim ievietot" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Funkcijas:" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "AnimationPlayer nevar animÄ“t pats sevi, tikai citus spÄ“lÄ“tÄjus." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "property '%s'" +msgstr "" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Anim Izveidot un Ievietot" @@ -950,7 +967,7 @@ msgstr "Izveidot Jaunu %s" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2270,6 +2287,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -3051,10 +3079,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "NepÄrtraukti Atjaunot" @@ -3665,6 +3689,15 @@ msgid "Download from:" msgstr "" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "AtvÄ“rt Failu PÄrlÅ«kÄ" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8410,6 +8443,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Noņemt no FavorÄ«tiem" @@ -8438,6 +8477,12 @@ msgid "Remove All StyleBox Items" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Pievienot FavorÄ«tiem" @@ -11935,6 +11980,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13556,6 +13609,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/mi.po b/editor/translations/mi.po index 021c55f38b..3a70aade1a 100644 --- a/editor/translations/mi.po +++ b/editor/translations/mi.po @@ -329,6 +329,7 @@ msgstr "" msgid "Remove Anim Track" msgstr "" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "" @@ -353,10 +354,25 @@ msgstr "" msgid "Anim Insert" msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "animation" +msgstr "" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "property '%s'" +msgstr "" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "" @@ -911,7 +927,7 @@ msgstr "" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2202,6 +2218,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -2981,10 +3008,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "" @@ -3589,6 +3612,14 @@ msgid "Download from:" msgstr "" #: editor/export_template_manager.cpp +msgid "Open in Web Browser" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8266,6 +8297,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Remove All Color Items" msgstr "" @@ -8290,6 +8327,12 @@ msgid "Remove All StyleBox Items" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Add Color Item" msgstr "" @@ -11697,6 +11740,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13291,6 +13342,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/mk.po b/editor/translations/mk.po index a437ddf1c7..bf449381bb 100644 --- a/editor/translations/mk.po +++ b/editor/translations/mk.po @@ -336,6 +336,7 @@ msgstr "" msgid "Remove Anim Track" msgstr "" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "" @@ -360,10 +361,25 @@ msgstr "" msgid "Anim Insert" msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "animation" +msgstr "" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "property '%s'" +msgstr "" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "" @@ -918,7 +934,7 @@ msgstr "" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2210,6 +2226,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -2989,10 +3016,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "" @@ -3598,6 +3621,14 @@ msgid "Download from:" msgstr "" #: editor/export_template_manager.cpp +msgid "Open in Web Browser" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8281,6 +8312,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Remove All Color Items" msgstr "" @@ -8305,6 +8342,12 @@ msgid "Remove All StyleBox Items" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Add Color Item" msgstr "" @@ -11712,6 +11755,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13306,6 +13357,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/ml.po b/editor/translations/ml.po index 8213c2251c..b0d3a5a8d7 100644 --- a/editor/translations/ml.po +++ b/editor/translations/ml.po @@ -339,6 +339,7 @@ msgstr "" msgid "Remove Anim Track" msgstr "" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "" @@ -363,10 +364,27 @@ msgstr "" msgid "Anim Insert" msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "ചലനം à´šàµà´±àµà´±àµ½" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "à´—àµà´£à´‚ നോകàµà´•àµà´•" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "" @@ -921,7 +939,7 @@ msgstr "" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2215,6 +2233,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -2994,10 +3023,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "" @@ -3602,6 +3627,14 @@ msgid "Download from:" msgstr "" #: editor/export_template_manager.cpp +msgid "Open in Web Browser" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8287,6 +8320,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Remove All Color Items" msgstr "" @@ -8311,6 +8350,12 @@ msgid "Remove All StyleBox Items" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Add Color Item" msgstr "" @@ -11719,6 +11764,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13315,6 +13368,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/mr.po b/editor/translations/mr.po index b459ca23f3..af59635c8a 100644 --- a/editor/translations/mr.po +++ b/editor/translations/mr.po @@ -336,6 +336,7 @@ msgstr "" msgid "Remove Anim Track" msgstr "" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "" @@ -360,10 +361,26 @@ msgstr "" msgid "Anim Insert" msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "अâ€à¥…निमेशन टà¥à¤°à¥€" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "property '%s'" +msgstr "" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "" @@ -918,7 +935,7 @@ msgstr "" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2209,6 +2226,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -2989,10 +3017,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "" @@ -3597,6 +3621,14 @@ msgid "Download from:" msgstr "" #: editor/export_template_manager.cpp +msgid "Open in Web Browser" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8279,6 +8311,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Remove All Color Items" msgstr "" @@ -8303,6 +8341,12 @@ msgid "Remove All StyleBox Items" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Add Color Item" msgstr "" @@ -11713,6 +11757,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13308,6 +13360,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/ms.po b/editor/translations/ms.po index 51a09d2e24..5fd2547bcb 100644 --- a/editor/translations/ms.po +++ b/editor/translations/ms.po @@ -344,6 +344,7 @@ msgstr "Tukar Mod Gelung Animasi" msgid "Remove Anim Track" msgstr "Keluarkan Trek Anim" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Cipta trek BARU untuk %s dan masukkan kunci?" @@ -368,10 +369,27 @@ msgstr "Cipta" msgid "Anim Insert" msgstr "Masukkan Anim" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Set Peralihan ke:" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "AnimationPlayer tidak animasikan dirinya sendiri, hanya pemain lain." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "Sifat" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Anim Cipta & Masukkan" @@ -950,7 +968,7 @@ msgstr "Cipta %s Baru" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2302,6 +2320,17 @@ msgid "New Window" msgstr "Tetingkap Baru" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "Berputar apabila tingkap editor dilukis semula." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Sumber yang diimport tidak dapat disimpan." @@ -3165,10 +3194,6 @@ msgid "Save & Restart" msgstr "Simpan & Mula Semula" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "Berputar apabila tingkap editor dilukis semula." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "Kemas Kini Secara Berterusan" @@ -3828,6 +3853,15 @@ msgid "Download from:" msgstr "Muat turun" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Buka dalam Pengurus Fail" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8593,6 +8627,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Keluarkan Item" @@ -8623,6 +8663,12 @@ msgid "Remove All StyleBox Items" msgstr "Keluarkan Item" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Tambah ke Kegemaran" @@ -12087,6 +12133,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13712,6 +13766,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/nb.po b/editor/translations/nb.po index efbca6253d..02f32b055b 100644 --- a/editor/translations/nb.po +++ b/editor/translations/nb.po @@ -353,6 +353,7 @@ msgstr "Endre løkkemodus for animasjon" msgid "Remove Anim Track" msgstr "Fjern Anim-Spor" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Lag NYTT spor for %s og sett inn nøkkel?" @@ -377,10 +378,28 @@ msgstr "Lag" msgid "Anim Insert" msgstr "Anim Sett inn" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "Kan ikke Ã¥pne '%s'." + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animasjon" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "AnimasjonAvspiller kan ikke animere seg selv, kun andre avspillere." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "Egenskapen «%s» eksisterer ikke." + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Anim Lag og Sett Inn" @@ -971,7 +990,7 @@ msgstr "Lag ny %s" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2384,6 +2403,17 @@ msgid "New Window" msgstr "Nytt vindu" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "Snurrer nÃ¥r redigeringsvinduet tegner opp pÃ¥ nytt." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Importerte ressurser kan ikke lagres." @@ -3260,10 +3290,6 @@ msgid "Save & Restart" msgstr "Lagre & Avslutt" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "Snurrer nÃ¥r redigeringsvinduet tegner opp pÃ¥ nytt." - -#: editor/editor_node.cpp #, fuzzy msgid "Update Continuously" msgstr "Kontinuerlig" @@ -3931,6 +3957,16 @@ msgid "Download from:" msgstr "Nedlastningsfeil" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Vis I Filutforsker" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "Kopier feil" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8990,6 +9026,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Fjern Funksjon" @@ -9020,6 +9062,12 @@ msgid "Remove All StyleBox Items" msgstr "Fjern Funksjon" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Legg til Element" @@ -12688,6 +12736,16 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "Fjern Funksjon" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "Fjern Funksjon" + #: modules/csg/csg_gizmos.cpp #, fuzzy msgid "Change Cylinder Radius" @@ -14382,6 +14440,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" @@ -15259,9 +15353,6 @@ msgstr "Konstanter kan ikke endres." #~ msgid "I see..." #~ msgstr "Jeg forstÃ¥r..." -#~ msgid "Can't open '%s'." -#~ msgstr "Kan ikke Ã¥pne '%s'." - #~ msgid "Ugh" #~ msgstr "Æsj" diff --git a/editor/translations/nl.po b/editor/translations/nl.po index 2f16b90840..00f87ef79c 100644 --- a/editor/translations/nl.po +++ b/editor/translations/nl.po @@ -48,12 +48,13 @@ # Arthur de Roos <arthur.de.roos@gmail.com>, 2021. # Vancha March <tjipkevdh@gmail.com>, 2021. # Hugo van de Kuilen <hugo.vandekuilen1234567890@gmail.com>, 2021. +# tobeqz <vanveenjorik+tobeqz@gmail.com>, 2021. msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2021-08-02 02:00+0000\n" -"Last-Translator: Hugo van de Kuilen <hugo.vandekuilen1234567890@gmail.com>\n" +"PO-Revision-Date: 2021-08-06 19:42+0000\n" +"Last-Translator: tobeqz <vanveenjorik+tobeqz@gmail.com>\n" "Language-Team: Dutch <https://hosted.weblate.org/projects/godot-engine/godot/" "nl/>\n" "Language: nl\n" @@ -384,6 +385,7 @@ msgstr "Animatielusmodus veranderen" msgid "Remove Anim Track" msgstr "Verwijder Anim Track" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "NIEUW spoor aanmaken voor %s en sleutel invoegen?" @@ -408,10 +410,28 @@ msgstr "Maken" msgid "Anim Insert" msgstr "Anim Invoegen" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "Kan '%s' niet openen." + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animatie" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "Animatie-Speler kan zichzelf niet animeren, alleen andere spelers." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "Eigenschap '%s' bestaat niet." + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Anim Maken & Invoegen" @@ -622,9 +642,8 @@ msgid "Go to Previous Step" msgstr "Ga naar Vorige Stap" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Apply Reset" -msgstr "Resetten" +msgstr "Reset" #: editor/animation_track_editor.cpp msgid "Optimize Animation" @@ -643,9 +662,8 @@ msgid "Use Bezier Curves" msgstr "Gebruik Bezier Curves" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Create RESET Track(s)" -msgstr "Plak sporen" +msgstr "Creëer RESET Track(s)" #: editor/animation_track_editor.cpp msgid "Anim. Optimizer" @@ -992,9 +1010,9 @@ msgstr "%s opstellen" msgid "No results for \"%s\"." msgstr "Geen resultaten voor \"%s\"." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." -msgstr "" +msgstr "Geen beschrijving beschikbaar voor %s." #: editor/create_dialog.cpp editor/editor_file_dialog.cpp #: editor/filesystem_dock.cpp @@ -1094,7 +1112,6 @@ msgid "Owners Of:" msgstr "Eigenaren van:" #: editor/dependency_editor.cpp -#, fuzzy msgid "" "Remove the selected files from the project? (Cannot be undone.)\n" "Depending on your filesystem configuration, the files will either be moved " @@ -1102,11 +1119,10 @@ msgid "" msgstr "" "Geselecteerde bestanden uit het project verwijderen? (Kan niet ongedaan " "gemaakt worden)\n" -"De bestanden kunnen mogelijk vanuit de prullenbak van het systeem hersteld " -"worden." +"Bestanden kunnen naar de prullenbak gestuurd worden of permanent verwijderd " +"worden, afhankelijk van uw bestandssysteem." #: editor/dependency_editor.cpp -#, fuzzy msgid "" "The files being removed are required by other resources in order for them to " "work.\n" @@ -1117,8 +1133,8 @@ msgstr "" "De bestanden die verwijderd worden zijn nodig om andere bronnen te laten " "werken.\n" "Toch verwijderen? (Onomkeerbaar)\n" -"De bestanden kunnen mogelijk vanuit de prullenbak van het systeem hersteld " -"worden." +"De bestanden kunnen naar de prullenbak gestuurd worden of permanent " +"verwijderd worden, afhankelijk van uw bestandssysteem." #: editor/dependency_editor.cpp msgid "Cannot remove:" @@ -1288,7 +1304,6 @@ msgid "Licenses" msgstr "Licenties" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Error opening asset file for \"%s\" (not in ZIP format)." msgstr "Fout bij het openen van het pakketbestand, geen zip-formaat." @@ -1300,29 +1315,28 @@ msgstr "%s (bestaat al)" #: editor/editor_asset_installer.cpp msgid "Contents of asset \"%s\" - %d file(s) conflict with your project:" msgstr "" +"Inhoud van asset \"%s\" - %d bestand(en) zijn in conflict met uw project:" #: editor/editor_asset_installer.cpp msgid "Contents of asset \"%s\" - No files conflict with your project:" msgstr "" +"Inhoud van asset \"%s\" - Geen bestanden hebben een conflict met uw project:" #: editor/editor_asset_installer.cpp msgid "Uncompressing Assets" msgstr "Bronnen aan het uitpakken" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "The following files failed extraction from asset \"%s\":" -msgstr "De volgende bestanden konden niet worden uitgepakt:" +msgstr "De volgende bestanden konden niet worden uitgepakt uit \"%s\":" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "(and %s more files)" -msgstr "En nog %s bestand(en)." +msgstr "(en nog %s bestanden)" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Asset \"%s\" installed successfully!" -msgstr "Pakket succesvol geïnstalleerd!" +msgstr "Asset \"%s\" succesvol geïnstalleerd!" #: editor/editor_asset_installer.cpp #: editor/plugins/asset_library_editor_plugin.cpp @@ -1334,9 +1348,8 @@ msgid "Install" msgstr "Installeer" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Asset Installer" -msgstr "Pakketinstalleerder" +msgstr "Assetinstalleerder" #: editor/editor_audio_buses.cpp msgid "Speakers" @@ -1399,7 +1412,6 @@ msgid "Bypass" msgstr "Omleiden" #: editor/editor_audio_buses.cpp -#, fuzzy msgid "Bus Options" msgstr "Audiobusopties" @@ -1567,13 +1579,12 @@ msgid "Can't add autoload:" msgstr "Autoload kan niet toevoegd worden:" #: editor/editor_autoload_settings.cpp -#, fuzzy msgid "%s is an invalid path. File does not exist." -msgstr "Bestand bestaat niet." +msgstr "%s is een ongeldig pad. Bestand bestaat niet." #: editor/editor_autoload_settings.cpp msgid "%s is an invalid path. Not in resource path (res://)." -msgstr "" +msgstr "%s is een ongeldig pad. Niet in bron pad (res://)." #: editor/editor_autoload_settings.cpp msgid "Add AutoLoad" @@ -1773,7 +1784,7 @@ msgstr "Importtabblad" #: editor/editor_feature_profile.cpp msgid "Allows to view and edit 3D scenes." -msgstr "" +msgstr "Laat u 3D scenes weergeven en bewerken." #: editor/editor_feature_profile.cpp msgid "Allows to edit scripts using the integrated script editor." @@ -2345,6 +2356,17 @@ msgid "New Window" msgstr "Nieuw Venster" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "Draait wanneer het editor venster wordt hertekend." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Geïmporteerde bronnen kunnen niet opgeslagen worden." @@ -3205,10 +3227,6 @@ msgid "Save & Restart" msgstr "Opslaan & Herstarten" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "Draait wanneer het editor venster wordt hertekend." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "Continu Bijwerken" @@ -3874,6 +3892,16 @@ msgid "Download from:" msgstr "Downloadfout" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Uitvoeren in Browser" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "Kopieer Fout" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8745,6 +8773,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Verwijder Alle Items" @@ -8775,6 +8809,12 @@ msgid "Remove All StyleBox Items" msgstr "Verwijder Alle Items" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Class Items Toevoegen" @@ -12447,6 +12487,16 @@ msgstr "Wijzig Cylinder Vorm Hoogte" msgid "Change Ray Shape Length" msgstr "Wijzig Ray Vorm Lengte" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "Zet Curve Punt Positie" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "Zet Curve Punt Positie" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "Wijzig Cylinder Straal" @@ -14216,6 +14266,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "Dit lichaam zal worden genegeerd totdat je een mesh instelt." @@ -15415,9 +15501,6 @@ msgstr "Constanten kunnen niet worden aangepast." #~ msgid "I see..." #~ msgstr "Ik snap het..." -#~ msgid "Can't open '%s'." -#~ msgstr "Kan '%s' niet openen." - #~ msgid "Ugh" #~ msgstr "Oeps" diff --git a/editor/translations/or.po b/editor/translations/or.po index 89cbdfbf2b..8bee62f8d5 100644 --- a/editor/translations/or.po +++ b/editor/translations/or.po @@ -335,6 +335,7 @@ msgstr "" msgid "Remove Anim Track" msgstr "" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "" @@ -359,10 +360,25 @@ msgstr "" msgid "Anim Insert" msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "animation" +msgstr "" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "property '%s'" +msgstr "" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "" @@ -917,7 +933,7 @@ msgstr "" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2208,6 +2224,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -2987,10 +3014,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "" @@ -3595,6 +3618,14 @@ msgid "Download from:" msgstr "" #: editor/export_template_manager.cpp +msgid "Open in Web Browser" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8272,6 +8303,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Remove All Color Items" msgstr "" @@ -8296,6 +8333,12 @@ msgid "Remove All StyleBox Items" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Add Color Item" msgstr "" @@ -11703,6 +11746,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13297,6 +13348,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/pl.po b/editor/translations/pl.po index edaf8ab701..24ad379ad0 100644 --- a/editor/translations/pl.po +++ b/editor/translations/pl.po @@ -384,6 +384,7 @@ msgstr "ZmieÅ„ sposób zapÄ™tlania animacji" msgid "Remove Anim Track" msgstr "UsuÅ„ Å›cieżkÄ™ animacji" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Utworzyć NOWÄ„ Å›cieżkÄ™ dla %s i wstawić klucz?" @@ -408,11 +409,29 @@ msgstr "Utwórz" msgid "Anim Insert" msgstr "Wstaw animacjÄ™" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "Nie można otworzyć '%s'." + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animacja" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" "AnimationPlayer nie może animować sam siebie, tylko inne wÄ™zÅ‚y tego typu." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "WÅ‚aÅ›ciwość \"%s\" nie istnieje." + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Utwórz i wstaw" @@ -987,7 +1006,7 @@ msgstr "Utwórz nowy %s" msgid "No results for \"%s\"." msgstr "Brak wyników dla \"%s\"." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2333,6 +2352,17 @@ msgid "New Window" msgstr "Nowe okno" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "Obraca siÄ™, gdy okno edytora jest przerysowywane." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Zaimportowane zasoby nie mogÄ… być zapisane." @@ -3192,10 +3222,6 @@ msgid "Save & Restart" msgstr "Zapisz i zrestartuj" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "Obraca siÄ™, gdy okno edytora jest przerysowywane." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "Aktualizuj ciÄ…gle" @@ -3856,6 +3882,16 @@ msgid "Download from:" msgstr "Błąd pobierania" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Uruchom w przeglÄ…darce" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "Kopiuj błąd" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8727,6 +8763,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "UsuÅ„ wszystkie elementy" @@ -8757,6 +8799,12 @@ msgid "Remove All StyleBox Items" msgstr "UsuÅ„ wszystkie elementy" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Dodaj klasÄ™ elementów" @@ -12419,6 +12467,16 @@ msgstr "ZmieÅ„ wysokość ksztaÅ‚tu cylindra" msgid "Change Ray Shape Length" msgstr "Zmień dÅ‚ugość Ray Shape" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "Ustaw pozycje punktu krzywej" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "Ustaw pozycje punktu krzywej" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "ZmieÅ„ promień cylindra" @@ -14200,6 +14258,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "To ciaÅ‚o bÄ™dzie ignorowane, dopóki nie ustawisz siatki." @@ -15477,9 +15571,6 @@ msgstr "StaÅ‚e nie mogÄ… być modyfikowane." #~ msgid "I see..." #~ msgstr "WidzÄ™..." -#~ msgid "Can't open '%s'." -#~ msgstr "Nie można otworzyć '%s'." - #~ msgid "Ugh" #~ msgstr "Błąd" diff --git a/editor/translations/pr.po b/editor/translations/pr.po index 1bcfe47610..96fab899cd 100644 --- a/editor/translations/pr.po +++ b/editor/translations/pr.po @@ -360,6 +360,7 @@ msgstr "" msgid "Remove Anim Track" msgstr "" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "" @@ -384,10 +385,26 @@ msgstr "" msgid "Anim Insert" msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Yer functions:" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "property '%s'" +msgstr "" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "" @@ -962,7 +979,7 @@ msgstr "" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2291,6 +2308,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -3086,10 +3114,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "" @@ -3720,6 +3744,15 @@ msgid "Download from:" msgstr "Discharge ye' Variable" #: editor/export_template_manager.cpp +msgid "Open in Web Browser" +msgstr "" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "Slit th' Node" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8562,6 +8595,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Discharge ye' Variable" @@ -8592,6 +8631,12 @@ msgid "Remove All StyleBox Items" msgstr "Discharge ye' Variable" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Add Node" @@ -12150,6 +12195,16 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "Discharge ye' Signal" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "Discharge ye' Signal" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13820,6 +13875,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/pt.po b/editor/translations/pt.po index e51c7a3b77..1c8e2476a3 100644 --- a/editor/translations/pt.po +++ b/editor/translations/pt.po @@ -23,8 +23,8 @@ msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2021-07-03 10:33+0000\n" -"Last-Translator: André Silva <andre.olivais@gmail.com>\n" +"PO-Revision-Date: 2021-08-06 06:48+0000\n" +"Last-Translator: João Lopes <linux-man@hotmail.com>\n" "Language-Team: Portuguese <https://hosted.weblate.org/projects/godot-engine/" "godot/pt/>\n" "Language: pt\n" @@ -354,6 +354,7 @@ msgstr "Mudar Modo do Loop da Animação" msgid "Remove Anim Track" msgstr "Remover Pista de Animação" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Criar NOVA pista para %s e inserir chave?" @@ -378,12 +379,30 @@ msgstr "Criar" msgid "Anim Insert" msgstr "Anim Inserir" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "ImpossÃvel abrir '%s'." + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animação" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" "AnimationPlayer não se pode animar a ele próprio, apenas a outros " "executantes." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "Não existe a Propriedade '%s'." + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Anim Criar & Inserir" @@ -591,9 +610,8 @@ msgid "Go to Previous Step" msgstr "Ir para Passo Anterior" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Apply Reset" -msgstr "Repor" +msgstr "Aplicar Reinicialização" #: editor/animation_track_editor.cpp msgid "Optimize Animation" @@ -612,9 +630,8 @@ msgid "Use Bezier Curves" msgstr "Usar Curvas Bezier" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Create RESET Track(s)" -msgstr "Colar Pistas" +msgstr "Criar Pista(s) RESET" #: editor/animation_track_editor.cpp msgid "Anim. Optimizer" @@ -939,7 +956,6 @@ msgid "Edit..." msgstr "Editar..." #: editor/connections_dialog.cpp -#, fuzzy msgid "Go to Method" msgstr "Ir para Método" @@ -959,9 +975,9 @@ msgstr "Criar Novo %s" msgid "No results for \"%s\"." msgstr "Nenhum resultado para \"%s\"." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." -msgstr "" +msgstr "Nenhuma descrição disponÃvel para %s." #: editor/create_dialog.cpp editor/editor_file_dialog.cpp #: editor/filesystem_dock.cpp @@ -1061,17 +1077,16 @@ msgid "Owners Of:" msgstr "Proprietários de:" #: editor/dependency_editor.cpp -#, fuzzy msgid "" "Remove the selected files from the project? (Cannot be undone.)\n" "Depending on your filesystem configuration, the files will either be moved " "to the system trash or deleted permanently." msgstr "" -"Remover ficheiros selecionados do Projeto? (sem desfazer)\n" -"Pode encontrar os ficheiros removidos na Reciclagem do sistema." +"Remover ficheiros selecionados do Projeto? (Sem desfazer.)\n" +"Dependendo da configuração, pode encontrar os ficheiros removidos na " +"Reciclagem do sistema ou apagados permanentemente." #: editor/dependency_editor.cpp -#, fuzzy msgid "" "The files being removed are required by other resources in order for them to " "work.\n" @@ -1081,8 +1096,9 @@ msgid "" msgstr "" "Os ficheiros a serem removidos são necessários para que outros recursos " "funcionem.\n" -"Remover mesmo assim? (sem desfazer)\n" -"Pode encontrar os ficheiros removidos na Reciclagem do sistema." +"Remover mesmo assim? (Sem desfazer.)\n" +"Dependendo da configuração, pode encontrar os ficheiros removidos na " +"Reciclagem do sistema ou apagados permanentemente." #: editor/dependency_editor.cpp msgid "Cannot remove:" @@ -1252,14 +1268,13 @@ msgid "Licenses" msgstr "Licenças" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Error opening asset file for \"%s\" (not in ZIP format)." -msgstr "Erro ao abrir ficheiro comprimido (não está no formato ZIP)." +msgstr "" +"Erro ao abrir ficheiro de recurso para \"%s\" (não está no formato ZIP)." #: editor/editor_asset_installer.cpp -#, fuzzy msgid "%s (already exists)" -msgstr "%s (Já Existe)" +msgstr "%s (já existe)" #: editor/editor_asset_installer.cpp msgid "Contents of asset \"%s\" - %d file(s) conflict with your project:" @@ -1274,19 +1289,16 @@ msgid "Uncompressing Assets" msgstr "A Descomprimir Ativos" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "The following files failed extraction from asset \"%s\":" -msgstr "Falhou a extração dos seguintes Ficheiros do pacote:" +msgstr "Falhou a extração dos seguintes ficheiros do recurso \"%s\":" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "(and %s more files)" -msgstr "E mais %s ficheiros." +msgstr "(e mais %s ficheiros)" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Asset \"%s\" installed successfully!" -msgstr "Pacote Instalado com sucesso!" +msgstr "Recurso \"%s\" instalado com sucesso!" #: editor/editor_asset_installer.cpp #: editor/plugins/asset_library_editor_plugin.cpp @@ -1298,9 +1310,8 @@ msgid "Install" msgstr "Instalar" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Asset Installer" -msgstr "Instalador de Pacotes" +msgstr "Instalador de Recursos" #: editor/editor_audio_buses.cpp msgid "Speakers" @@ -2310,6 +2321,17 @@ msgid "New Window" msgstr "Nova Janela" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "Roda quando a janela do editor atualiza." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Recursos importados não podem ser guardados." @@ -3171,10 +3193,6 @@ msgid "Save & Restart" msgstr "Guardar & Reiniciar" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "Roda quando a janela do editor atualiza." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "Atualização ContÃnua" @@ -3839,6 +3857,16 @@ msgid "Download from:" msgstr "Erro na transferência" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Executar no Navegador" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "Copiar Erro" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8698,6 +8726,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Remover Todos os Itens" @@ -8728,6 +8762,12 @@ msgid "Remove All StyleBox Items" msgstr "Remover Todos os Itens" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Adicionar Itens de Classe" @@ -12385,6 +12425,16 @@ msgstr "Mudar Altura da Forma Cilindro" msgid "Change Ray Shape Length" msgstr "Mudar comprimento da forma raio" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "Definir posição do Ponto da curva" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "Definir posição do Ponto da curva" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "Mudar Raio do Cilindro" @@ -14158,6 +14208,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "Este corpo será ignorado até se definir uma malha." @@ -15596,9 +15682,6 @@ msgstr "Constantes não podem ser modificadas." #~ msgid "I see..." #~ msgstr "Eu vejo..." -#~ msgid "Can't open '%s'." -#~ msgstr "ImpossÃvel abrir '%s'." - #~ msgid "Ugh" #~ msgstr "Ugh" diff --git a/editor/translations/pt_BR.po b/editor/translations/pt_BR.po index c11e4f347b..b7bb7ce0c4 100644 --- a/editor/translations/pt_BR.po +++ b/editor/translations/pt_BR.po @@ -95,14 +95,14 @@ # Felipe Jesus Macedo <fmacedo746@gmail.com>, 2020. # José Paulo <jose.paulo1919@gmail.com>, 2020. # Necco <necco@outlook.com>, 2020. -# Marcelo Silveira Hayden <mshayden.1998@gmail.com>, 2020. +# Marcelo Silveira Hayden <mshayden.1998@gmail.com>, 2020, 2021. # GUILHERME SOUZA REIS DE MELO LOPES <guilhermesrml@unipam.edu.br>, 2020. # Gabriela Araújo <Gabirin@outlook.com.br>, 2020. # Jairo Tuboi <tuboi.jairo@gmail.com>, 2020. # Felipe Fetter <felipetfetter@gmail.com>, 2020. # Rafael Henrique Capati <rhcapati@gmail.com>, 2020. # NogardRyuu <nogardryuu@gmail.com>, 2020, 2021. -# Elton <eltondeoliveira@outlook.com>, 2020. +# Elton <eltondeoliveira@outlook.com>, 2020, 2021. # ThiagoCTN <thiagocampostn@gmail.com>, 2020. # Alec Santos <alecsantos96@gmail.com>, 2020. # Augusto Milão <augusto.milao01@gmail.com>, 2021. @@ -118,12 +118,14 @@ # Gustavo HM 102 <gustavohm102@gmail.com>, 2021. # Douglas Leão <djlsplays@gmail.com>, 2021. # PauloFRs <paulofr1@hotmail.com>, 2021. +# Diego Bloise <diego-dev@outlook.com>, 2021. +# Alkoarism <Alkoarism@gmail.com>, 2021. msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: 2016-05-30\n" -"PO-Revision-Date: 2021-06-29 08:04+0000\n" -"Last-Translator: PauloFRs <paulofr1@hotmail.com>\n" +"PO-Revision-Date: 2021-08-06 06:47+0000\n" +"Last-Translator: Alkoarism <Alkoarism@gmail.com>\n" "Language-Team: Portuguese (Brazil) <https://hosted.weblate.org/projects/" "godot-engine/godot/pt_BR/>\n" "Language: pt_BR\n" @@ -131,7 +133,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" -"X-Generator: Weblate 4.7.1-dev\n" +"X-Generator: Weblate 4.8-dev\n" #: core/math/expression.cpp modules/gdscript/gdscript_functions.cpp #: modules/visual_script/visual_script_builtin_funcs.cpp @@ -452,6 +454,7 @@ msgstr "Alterar Modo Repetição da Animação" msgid "Remove Anim Track" msgstr "Remover Trilha da Anim" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Criar NOVA faixa para %s e inserir chave?" @@ -476,10 +479,28 @@ msgstr "Criar" msgid "Anim Insert" msgstr "Inserir Anim" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "Não é possÃvel abrir '%s'." + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animação" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "AnimationPlayer não pode animar a si mesmo, apenas outros jogadores." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "Nenhuma propriedade '%s' existe." + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Criar e Inserir Anim" @@ -689,9 +710,8 @@ msgid "Go to Previous Step" msgstr "Ir ao Passo Anterior" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Apply Reset" -msgstr "Recompor" +msgstr "Redefinir" #: editor/animation_track_editor.cpp msgid "Optimize Animation" @@ -710,9 +730,8 @@ msgid "Use Bezier Curves" msgstr "Usar Curvas de Bezier" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Create RESET Track(s)" -msgstr "Colar Trilhas" +msgstr "Criar RESET Track(s)" #: editor/animation_track_editor.cpp msgid "Anim. Optimizer" @@ -785,7 +804,7 @@ msgstr "Mudar Deslocamento do InÃcio do Clip de Trilha de Audio" #: editor/animation_track_editor_plugins.cpp msgid "Change Audio Track Clip End Offset" -msgstr "Alterar Deslocamento de Fim do Clipe de Faixa de Ãudio" +msgstr "Alterar fim da Trilha de Aúdio" #: editor/array_property_edit.cpp msgid "Resize Array" @@ -1056,9 +1075,9 @@ msgstr "Criar Novo %s" msgid "No results for \"%s\"." msgstr "Sem resultados para \"%s\"." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." -msgstr "" +msgstr "Sem descrição disponÃvel para %s." #: editor/create_dialog.cpp editor/editor_file_dialog.cpp #: editor/filesystem_dock.cpp @@ -1158,18 +1177,16 @@ msgid "Owners Of:" msgstr "Donos De:" #: editor/dependency_editor.cpp -#, fuzzy msgid "" "Remove the selected files from the project? (Cannot be undone.)\n" "Depending on your filesystem configuration, the files will either be moved " "to the system trash or deleted permanently." msgstr "" -"Remover arquivos selecionados do projeto? (irreversÃvel)\n" -"Você pode encontrar os arquivos removidos na lixeira do sistema para " -"restaurá-los." +"Remover arquivos selecionados do projeto? (IrreversÃvel.)\n" +"Dependendo da configuração do seu sistema, os arquivos serão movidos para a " +"lixeira do sistema ou apagados permanentemente." #: editor/dependency_editor.cpp -#, fuzzy msgid "" "The files being removed are required by other resources in order for them to " "work.\n" @@ -1177,11 +1194,11 @@ msgid "" "Depending on your filesystem configuration, the files will either be moved " "to the system trash or deleted permanently." msgstr "" -"Os arquivos sendo removidos são requeridos por outros recursos para que " -"funcionem.\n" -"Removê-los mesmo assim? (irreversÃvel)\n" -"Você pode encontrar os arquivos removidos na lixeira do sistema para " -"restaurá-los." +"Os arquivos que estão sendo removidos são necessários por outros recursos " +"para que funcionem.\n" +"Removê-los mesmo assim? (IrreversÃvel.)\n" +"Dependendo da configuração do seu sistema, os arquivos serão movidos para a " +"lixeira do sistema ou apagados permanentemente." #: editor/dependency_editor.cpp msgid "Cannot remove:" @@ -1351,9 +1368,8 @@ msgid "Licenses" msgstr "Licenças" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Error opening asset file for \"%s\" (not in ZIP format)." -msgstr "Erro ao abrir arquivo compactado (não está em formato ZIP)." +msgstr "Erro ao abrir o pacote \"%s\" (não está em formato ZIP)." #: editor/editor_asset_installer.cpp #, fuzzy @@ -1362,30 +1378,30 @@ msgstr "%s (Já existe)" #: editor/editor_asset_installer.cpp msgid "Contents of asset \"%s\" - %d file(s) conflict with your project:" -msgstr "" +msgstr "Conteúdo do asset \"%s\" - %d arquivo(s) conflita(m) com seu projeto:" #: editor/editor_asset_installer.cpp msgid "Contents of asset \"%s\" - No files conflict with your project:" msgstr "" +"Conteúdo do asset \"%s\" - Nenhum arquivo entra em conflito com o seu " +"projeto:" #: editor/editor_asset_installer.cpp msgid "Uncompressing Assets" msgstr "Descompactando Assets" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "The following files failed extraction from asset \"%s\":" -msgstr "Os arquivos a seguir falharam ao serem extraÃdos do pacote:" +msgstr "Os seguintes arquivos falharam na extração do asset \"% s\":" #: editor/editor_asset_installer.cpp #, fuzzy msgid "(and %s more files)" -msgstr "%s mais arquivo(s)." +msgstr "(e %s mais arquivos)" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Asset \"%s\" installed successfully!" -msgstr "Pacote instalado com sucesso!" +msgstr "Asset \"%s\" instalados com sucesso!" #: editor/editor_asset_installer.cpp #: editor/plugins/asset_library_editor_plugin.cpp @@ -1397,9 +1413,8 @@ msgid "Install" msgstr "Instalar" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Asset Installer" -msgstr "Instalador de Pacotes" +msgstr "Instalador de Assets" #: editor/editor_audio_buses.cpp msgid "Speakers" @@ -1632,11 +1647,11 @@ msgstr "Não pode adicionar autoload:" #: editor/editor_autoload_settings.cpp #, fuzzy msgid "%s is an invalid path. File does not exist." -msgstr "O arquivo não existe." +msgstr "O %s é um caminho inválido. O arquivo não existe." #: editor/editor_autoload_settings.cpp msgid "%s is an invalid path. Not in resource path (res://)." -msgstr "" +msgstr "%s é um caminho inválido. Não está no caminho dos recursos (res://)." #: editor/editor_autoload_settings.cpp msgid "Add AutoLoad" @@ -1662,7 +1677,7 @@ msgstr "Nome" #: editor/editor_autoload_settings.cpp #, fuzzy msgid "Global Variable" -msgstr "Variável" +msgstr "Variável Global" #: editor/editor_data.cpp msgid "Paste Params" @@ -1763,8 +1778,8 @@ msgid "" "Target platform requires 'PVRTC' texture compression for GLES2. Enable " "'Import Pvrtc' in Project Settings." msgstr "" -"A plataforma alvo requer compressão de texturas 'PVRTC' para GLES2. Habilite " -"'Importar Pvrtc' nas Configurações de Projeto." +"A plataforma de destino requer compressão de texturas 'PVRTC' para GLES2. " +"Habilite 'Importar Pvrtc' nas Configurações de Projeto." #: editor/editor_export.cpp msgid "" @@ -1836,36 +1851,43 @@ msgid "Import Dock" msgstr "Importar Dock" #: editor/editor_feature_profile.cpp +#, fuzzy msgid "Allows to view and edit 3D scenes." -msgstr "" +msgstr "Permite visualizar e editar cenas 3D." #: editor/editor_feature_profile.cpp +#, fuzzy msgid "Allows to edit scripts using the integrated script editor." -msgstr "" +msgstr "Permite editar scripts usando o editor de script integrado." #: editor/editor_feature_profile.cpp msgid "Provides built-in access to the Asset Library." -msgstr "" +msgstr "Fornece acesso integrado à Biblioteca de Assets." #: editor/editor_feature_profile.cpp msgid "Allows editing the node hierarchy in the Scene dock." -msgstr "" +msgstr "Permite editar a hierarquia de nó na doca Cena." #: editor/editor_feature_profile.cpp +#, fuzzy msgid "" "Allows to work with signals and groups of the node selected in the Scene " "dock." -msgstr "" +msgstr "Permite trabalhar com sinais e grupos do nó selecionado na doca Cena." #: editor/editor_feature_profile.cpp +#, fuzzy msgid "Allows to browse the local file system via a dedicated dock." msgstr "" +"Permite navegar pelo sistema de arquivos local através de uma doca dedicada." #: editor/editor_feature_profile.cpp msgid "" "Allows to configure import settings for individual assets. Requires the " "FileSystem dock to function." msgstr "" +"Permite definir as configurações de importação para assets individualmente. " +"Requer a doca FileSystem para funcionar." #: editor/editor_feature_profile.cpp #, fuzzy @@ -1874,11 +1896,11 @@ msgstr "(Atual)" #: editor/editor_feature_profile.cpp msgid "(none)" -msgstr "" +msgstr "(Nenhum(a))" #: editor/editor_feature_profile.cpp msgid "Remove currently selected profile, '%s'? Cannot be undone." -msgstr "" +msgstr "Remover o perfil selecionado, '%s'? Não pode ser desfeita." #: editor/editor_feature_profile.cpp msgid "Profile must be a valid filename and must not contain '.'" @@ -1911,17 +1933,16 @@ msgstr "Habilitar Editor Contextual" #: editor/editor_feature_profile.cpp #, fuzzy msgid "Class Properties:" -msgstr "Propriedades:" +msgstr "Propriedades de Classe:" #: editor/editor_feature_profile.cpp #, fuzzy msgid "Main Features:" -msgstr "Funcionalidades" +msgstr "CaracterÃsticas principais:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Nodes and Classes:" -msgstr "Classes Ativadas:" +msgstr "Nós e Classes:" #: editor/editor_feature_profile.cpp msgid "File '%s' format is invalid, import aborted." @@ -1939,9 +1960,8 @@ msgid "Error saving profile to path: '%s'." msgstr "Erro ao salvar perfil no caminho: '%s'." #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Reset to Default" -msgstr "Redefinir para os padrões" +msgstr "Redefinir padrões" #: editor/editor_feature_profile.cpp msgid "Current Profile:" @@ -1950,12 +1970,12 @@ msgstr "Perfil Atual:" #: editor/editor_feature_profile.cpp #, fuzzy msgid "Create Profile" -msgstr "Apagar Perfil" +msgstr "Criar Perfil" #: editor/editor_feature_profile.cpp #, fuzzy msgid "Remove Profile" -msgstr "Remover Telha" +msgstr "Remover Perfil" #: editor/editor_feature_profile.cpp msgid "Available Profiles:" @@ -1977,16 +1997,19 @@ msgstr "Exportação" #: editor/editor_feature_profile.cpp #, fuzzy msgid "Configure Selected Profile:" -msgstr "Perfil Atual:" +msgstr "Configurar Perfil Selecionado:" #: editor/editor_feature_profile.cpp #, fuzzy msgid "Extra Options:" -msgstr "Opções da Textura" +msgstr "Opções Extra:" #: editor/editor_feature_profile.cpp +#, fuzzy msgid "Create or import a profile to edit available classes and properties." msgstr "" +"Criar ou importar um perfil para editar as classes e propriedades " +"disponÃveis." #: editor/editor_feature_profile.cpp msgid "New profile name:" @@ -2015,7 +2038,7 @@ msgstr "Selecionar a Pasta Atual" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp #, fuzzy msgid "File exists, overwrite?" -msgstr "O arquivo existe. Sobrescrever?" +msgstr "O arquivo já existe. Sobrescrever?" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp msgid "Select This Folder" @@ -2408,6 +2431,17 @@ msgid "New Window" msgstr "Nova Janela" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "Gira quando a janela do editor atualiza." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Recursos Importados não podem ser salvos." @@ -2638,13 +2672,16 @@ msgid "" "The current scene has no root node, but %d modified external resource(s) " "were saved anyway." msgstr "" +"A cena atual não tem um nó raiz, mas %d recurso(s) externo(s) modificado(s) " +"foram salvos de qualquer forma." #: editor/editor_node.cpp -#, fuzzy msgid "" "A root node is required to save the scene. You can add a root node using the " "Scene tree dock." -msgstr "Um nó raiz é requerido para salvar a cena." +msgstr "" +"Um nó-raiz é necessário para salvar a cena. Você pode adicionar um nó-raiz " +"usando a doca da árvore de cenas." #: editor/editor_node.cpp msgid "Save Scene As..." @@ -2752,9 +2789,7 @@ msgstr "" #: editor/editor_node.cpp msgid "Unable to find script field for addon plugin at: '%s'." -msgstr "" -"Não foi possÃvel localizar a área do script para o complemento do plugin em: " -"'%s'." +msgstr "Não foi possÃvel encontrar o campo de script para o plugin em: '%s'." #: editor/editor_node.cpp msgid "Unable to load addon script from path: '%s'." @@ -3035,7 +3070,7 @@ msgstr "Explorador de Recursos Órfãos..." #: editor/editor_node.cpp #, fuzzy msgid "Reload Current Project" -msgstr "Renomear Projeto" +msgstr "Recarregar o projeto atual" #: editor/editor_node.cpp msgid "Quit to Project List" @@ -3197,11 +3232,12 @@ msgstr "Ajuda" #: editor/editor_node.cpp #, fuzzy msgid "Online Documentation" -msgstr "Abrir Documentação" +msgstr "Documentação Online" #: editor/editor_node.cpp +#, fuzzy msgid "Questions & Answers" -msgstr "" +msgstr "Perguntas & Respostas" #: editor/editor_node.cpp msgid "Report a Bug" @@ -3210,7 +3246,7 @@ msgstr "Reportar bug" #: editor/editor_node.cpp #, fuzzy msgid "Suggest a Feature" -msgstr "Defina um Valor" +msgstr "Sugira um recurso" #: editor/editor_node.cpp msgid "Send Docs Feedback" @@ -3223,7 +3259,7 @@ msgstr "Comunidade" #: editor/editor_node.cpp #, fuzzy msgid "About Godot" -msgstr "Sobre" +msgstr "Sobre Godot" #: editor/editor_node.cpp msgid "Support Godot Development" @@ -3275,10 +3311,6 @@ msgid "Save & Restart" msgstr "Salvar e Reiniciar" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "Gira quando a janela do editor atualiza." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "Atualizar Continuamente" @@ -3323,12 +3355,12 @@ msgstr "Gerenciar Templates" #: editor/editor_node.cpp #, fuzzy msgid "Install from file" -msgstr "Instalar a Partir do Arquivo" +msgstr "Instalar do arquivo" #: editor/editor_node.cpp #, fuzzy msgid "Select android sources file" -msgstr "Selecione uma Malha de origem:" +msgstr "Selecione o arquivo de fontes do Android" #: editor/editor_node.cpp msgid "" @@ -3412,7 +3444,7 @@ msgstr "Selecionar" #: editor/editor_node.cpp #, fuzzy msgid "Select Current" -msgstr "Selecionar a Pasta Atual" +msgstr "Selecione Atual" #: editor/editor_node.cpp msgid "Open 2D Editor" @@ -3449,7 +3481,7 @@ msgstr "Nenhum sub-recurso encontrado." #: editor/editor_path.cpp #, fuzzy msgid "Open a list of sub-resources." -msgstr "Nenhum sub-recurso encontrado." +msgstr "Abra uma lista de sub-recursos." #: editor/editor_plugin.cpp msgid "Creating Mesh Previews" @@ -3478,12 +3510,12 @@ msgstr "Atualizar" #: editor/editor_plugin_settings.cpp #, fuzzy msgid "Version" -msgstr "Versão:" +msgstr "Versão" #: editor/editor_plugin_settings.cpp #, fuzzy msgid "Author" -msgstr "Autores" +msgstr "Autor" #: editor/editor_plugin_settings.cpp #: editor/plugins/version_control_editor_plugin.cpp @@ -3496,14 +3528,12 @@ msgid "Measure:" msgstr "Medida:" #: editor/editor_profiler.cpp -#, fuzzy msgid "Frame Time (ms)" -msgstr "Tempo do Frame (seg)" +msgstr "Tempo do Frame (ms)" #: editor/editor_profiler.cpp -#, fuzzy msgid "Average Time (ms)" -msgstr "Tempo Médio (seg)" +msgstr "Tempo Médio (ms)" #: editor/editor_profiler.cpp msgid "Frame %" @@ -3945,6 +3975,16 @@ msgid "Download from:" msgstr "Erro ao baixar" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Rodar no Navegador" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "Copiar Erro" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -4294,15 +4334,15 @@ msgstr "Procurando..." #: editor/find_in_files.cpp msgid "%d match in %d file." -msgstr "%d correspondência em %d arquivo." +msgstr "%d correspondências no arquivo %d." #: editor/find_in_files.cpp msgid "%d matches in %d file." -msgstr "%d correspondências em %d arquivo." +msgstr "%d correspondências no arquivo %d." #: editor/find_in_files.cpp msgid "%d matches in %d files." -msgstr "%d correspondências em %d arquivos." +msgstr "%d correspondências no arquivo %d." #: editor/groups_editor.cpp msgid "Add to Group" @@ -4444,11 +4484,11 @@ msgstr "Selecione o arquivo para importar" #: editor/import_defaults_editor.cpp msgid "Importer:" -msgstr "Importar:" +msgstr "Importador:" #: editor/import_defaults_editor.cpp msgid "Reset to Defaults" -msgstr "Redefinir para os padrões" +msgstr "Redefinir padrões" #: editor/import_dock.cpp msgid "Keep File (No Import)" @@ -4766,7 +4806,7 @@ msgstr "Abrir Editor" #: editor/plugins/animation_blend_tree_editor_plugin.cpp #: editor/plugins/animation_state_machine_editor.cpp msgid "Open Animation Node" -msgstr "Abrir Animação de Nós" +msgstr "Abrir Nó de Animação" #: editor/plugins/animation_blend_space_2d_editor.cpp msgid "Triangle already exists." @@ -4833,7 +4873,7 @@ msgstr "Editar Filtros" #: editor/plugins/animation_blend_tree_editor_plugin.cpp msgid "Output node can't be added to the blend tree." -msgstr "Nós de saÃda não pode ser adicionado à árvore de mistura." +msgstr "Nó de SaÃda não pode ser adicionado à árvore de mistura." #: editor/plugins/animation_blend_tree_editor_plugin.cpp msgid "Add Node to BlendTree" @@ -8818,6 +8858,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Remover Todos os Itens" @@ -8848,6 +8894,12 @@ msgid "Remove All StyleBox Items" msgstr "Remover Todos os Itens" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Adicionar Itens de Classe" @@ -12508,6 +12560,16 @@ msgstr "Alterar a Altura da Forma do Cilindro" msgid "Change Ray Shape Length" msgstr "Alterar o Comprimento da Forma do Raio" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "Definir Posição do Ponto da Curva" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "Definir Posição do Ponto da Curva" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "Alterar Raio do Cilindro" @@ -14289,6 +14351,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "Este corpo será ignorado até você definir uma malha." @@ -15546,9 +15644,6 @@ msgstr "Constantes não podem serem modificadas." #~ msgid "I see..." #~ msgstr "Entendo..." -#~ msgid "Can't open '%s'." -#~ msgstr "Não é possÃvel abrir '%s'." - #~ msgid "Ugh" #~ msgstr "Ugh" diff --git a/editor/translations/ro.po b/editor/translations/ro.po index f1d65384fc..2b1626bfe2 100644 --- a/editor/translations/ro.po +++ b/editor/translations/ro.po @@ -351,6 +351,7 @@ msgstr "SchimbaÈ›i Bucla AnimaÈ›iei" msgid "Remove Anim Track" msgstr "Elimină Pista Anim" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "CreaÈ›i pistă NOUA pentru %s È™i inseraÈ›i cheie?" @@ -375,10 +376,28 @@ msgstr "CreaÈ›i" msgid "Anim Insert" msgstr "Anim InseraÈ›i" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "Mod Snap (%s)" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "AnimaÈ›ie" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "AnimationPlayer nu se poate anima singur, doar alÈ›i jucători." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "Proprietate" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Anim CreaÈ›i È™i InseraÈ›i" @@ -955,7 +974,7 @@ msgstr "CreaÈ›i %s Nou" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2315,6 +2334,17 @@ msgid "New Window" msgstr "Fereastră Nouă" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "Se roteÈ™te când fereastra editorului se redeschide." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Resursele importate nu pot fi salvate." @@ -3179,10 +3209,6 @@ msgid "Save & Restart" msgstr "Salvează È™i Restartează" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "Se roteÈ™te când fereastra editorului se redeschide." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "Actualizare continuă" @@ -3821,6 +3847,15 @@ msgid "Download from:" msgstr "Eroare Descărcare" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Execută în Browser" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8780,6 +8815,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "EliminaÈ›i Autoload" @@ -8810,6 +8851,12 @@ msgid "Remove All StyleBox Items" msgstr "EliminaÈ›i Autoload" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Adaugă Obiect" @@ -12407,6 +12454,16 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "Setare poziÈ›ie punct de curbă" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "Setare poziÈ›ie punct de curbă" + #: modules/csg/csg_gizmos.cpp #, fuzzy msgid "Change Cylinder Radius" @@ -14064,6 +14121,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" @@ -14580,9 +14673,6 @@ msgstr "" #~ msgid "Scale Mode (R)" #~ msgstr "Mod Redimensionare (R)" -#~ msgid "Snap Mode (%s)" -#~ msgstr "Mod Snap (%s)" - #~ msgid "Tool Scale" #~ msgstr "Unealtă Dimensiune" diff --git a/editor/translations/ru.po b/editor/translations/ru.po index a5d8b4ea1c..50d4484e4b 100644 --- a/editor/translations/ru.po +++ b/editor/translations/ru.po @@ -102,7 +102,7 @@ msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2021-07-29 02:33+0000\n" +"PO-Revision-Date: 2021-08-10 21:40+0000\n" "Last-Translator: Danil Alexeev <danil@alexeev.xyz>\n" "Language-Team: Russian <https://hosted.weblate.org/projects/godot-engine/" "godot/ru/>\n" @@ -112,7 +112,7 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" -"X-Generator: Weblate 4.7.2-dev\n" +"X-Generator: Weblate 4.8-dev\n" #: core/math/expression.cpp modules/gdscript/gdscript_functions.cpp #: modules/visual_script/visual_script_builtin_funcs.cpp @@ -435,6 +435,7 @@ msgstr "Изменить режим цикла анимации" msgid "Remove Anim Track" msgstr "Удалить дорожку" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Создать новую дорожку Ð´Ð»Ñ %s и вÑтавить ключ?" @@ -459,10 +460,28 @@ msgstr "Создать" msgid "Anim Insert" msgstr "Ð’Ñтавить" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "Ðе удаётÑÑ Ð¾Ñ‚ÐºÑ€Ñ‹Ñ‚ÑŒ '%s'." + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "ÐнимациÑ" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "AnimationPlayer не может анимировать Ñам ÑебÑ, только других." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "СвойÑтво «%s» не ÑущеÑтвует." + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Создать и вÑтавить" @@ -672,9 +691,8 @@ msgid "Go to Previous Step" msgstr "Перейти к предыдущему шагу" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Apply Reset" -msgstr "СброÑить" +msgstr "Применить ÑброÑ" #: editor/animation_track_editor.cpp msgid "Optimize Animation" @@ -693,9 +711,8 @@ msgid "Use Bezier Curves" msgstr "ИÑпользовать кривые Безье" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Create RESET Track(s)" -msgstr "Ð’Ñтавить треки" +msgstr "Создать дорожку(и) СБРОСÐ" #: editor/animation_track_editor.cpp msgid "Anim. Optimizer" @@ -1020,7 +1037,6 @@ msgid "Edit..." msgstr "Редактировать..." #: editor/connections_dialog.cpp -#, fuzzy msgid "Go to Method" msgstr "Перейти к методу" @@ -1040,9 +1056,9 @@ msgstr "Создать %s" msgid "No results for \"%s\"." msgstr "Ðет результатов Ð´Ð»Ñ Â«%s»." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." -msgstr "" +msgstr "Ðет опиÑÐ°Ð½Ð¸Ñ Ð´Ð»Ñ Â«%s»." #: editor/create_dialog.cpp editor/editor_file_dialog.cpp #: editor/filesystem_dock.cpp @@ -1142,17 +1158,16 @@ msgid "Owners Of:" msgstr "Владельцы:" #: editor/dependency_editor.cpp -#, fuzzy msgid "" "Remove the selected files from the project? (Cannot be undone.)\n" "Depending on your filesystem configuration, the files will either be moved " "to the system trash or deleted permanently." msgstr "" -"Удалить выбранные файлы из проекта? (ÐÐµÐ»ÑŒÐ·Ñ Ð¾Ñ‚Ð¼ÐµÐ½Ð¸Ñ‚ÑŒ)\n" -"Ð’Ñ‹ можете найти удалённые файлы в корзине, чтобы воÑÑтановить их." +"Удалить выбранные файлы из проекта? (ÐÐµÐ»ÑŒÐ·Ñ Ð¾Ñ‚Ð¼ÐµÐ½Ð¸Ñ‚ÑŒ.)\n" +"Ð’ завиÑимоÑти от конфигурации вашей файловой ÑиÑтемы файлы будут перемещены " +"в ÑиÑтемную корзину или удалены навÑегда." #: editor/dependency_editor.cpp -#, fuzzy msgid "" "The files being removed are required by other resources in order for them to " "work.\n" @@ -1161,8 +1176,9 @@ msgid "" "to the system trash or deleted permanently." msgstr "" "УдалÑемые файлы требуютÑÑ Ð´Ð»Ñ Ð¿Ñ€Ð°Ð²Ð¸Ð»ÑŒÐ½Ð¾Ð¹ работы других реÑурÑов.\n" -"Ð’ÑÑ‘ равно удалить их? (ÐÐµÐ»ÑŒÐ·Ñ Ð¾Ñ‚Ð¼ÐµÐ½Ð¸Ñ‚ÑŒ)\n" -"Ð’Ñ‹ можете найти удалённые файлы в корзине, чтобы воÑÑтановить их." +"Ð’ÑÑ‘ равно удалить их? (ÐÐµÐ»ÑŒÐ·Ñ Ð¾Ñ‚Ð¼ÐµÐ½Ð¸Ñ‚ÑŒ.)\n" +"Ð’ завиÑимоÑти от конфигурации вашей файловой ÑиÑтемы файлы будут перемещены " +"в ÑиÑтемную корзину или удалены навÑегда." #: editor/dependency_editor.cpp msgid "Cannot remove:" @@ -1332,41 +1348,36 @@ msgid "Licenses" msgstr "Лицензии" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Error opening asset file for \"%s\" (not in ZIP format)." -msgstr "Ошибка при открытии файла пакета (Ðе ÑвлÑетÑÑ ZIP форматом)." +msgstr "Ошибка при открытии файла реÑурÑа «%s» (не в формате ZIP)." #: editor/editor_asset_installer.cpp -#, fuzzy msgid "%s (already exists)" -msgstr "%s (Уже ÑущеÑтвует)" +msgstr "%s (уже ÑущеÑтвует)" #: editor/editor_asset_installer.cpp msgid "Contents of asset \"%s\" - %d file(s) conflict with your project:" -msgstr "" +msgstr "Содержимое реÑурÑа «%s» - %d файл(ов) конфликтуют Ñ Ð²Ð°ÑˆÐ¸Ð¼ проектом:" #: editor/editor_asset_installer.cpp msgid "Contents of asset \"%s\" - No files conflict with your project:" -msgstr "" +msgstr "Содержимое реÑурÑа «%s» - Ðет файлов, конфликтующих Ñ Ð²Ð°ÑˆÐ¸Ð¼ проектом:" #: editor/editor_asset_installer.cpp msgid "Uncompressing Assets" msgstr "РаÑпаковка аÑÑетов" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "The following files failed extraction from asset \"%s\":" -msgstr "Следующие файлы не удалоÑÑŒ извлечь из пакета:" +msgstr "Следующие файлы не удалоÑÑŒ извлечь из пакета «%s»:" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "(and %s more files)" -msgstr "Ещё %d файла(ов)." +msgstr "(ещё %d файла(ов))" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Asset \"%s\" installed successfully!" -msgstr "Пакет уÑпешно уÑтановлен!" +msgstr "РеÑÑƒÑ€Ñ Â«%s» уÑпешно уÑтановлен!" #: editor/editor_asset_installer.cpp #: editor/plugins/asset_library_editor_plugin.cpp @@ -1378,9 +1389,8 @@ msgid "Install" msgstr "УÑтановить" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Asset Installer" -msgstr "УÑтановщик пакетов" +msgstr "УÑтановщик реÑурÑов" #: editor/editor_audio_buses.cpp msgid "Speakers" @@ -1443,7 +1453,6 @@ msgid "Bypass" msgstr "Обход" #: editor/editor_audio_buses.cpp -#, fuzzy msgid "Bus Options" msgstr "Параметры шины" @@ -1611,13 +1620,12 @@ msgid "Can't add autoload:" msgstr "Ðе удаётÑÑ Ð´Ð¾Ð±Ð°Ð²Ð¸Ñ‚ÑŒ автозагрузку:" #: editor/editor_autoload_settings.cpp -#, fuzzy msgid "%s is an invalid path. File does not exist." -msgstr "Файл не ÑущеÑтвует." +msgstr "Ðеверный путь «%s». Файл не ÑущеÑтвует." #: editor/editor_autoload_settings.cpp msgid "%s is an invalid path. Not in resource path (res://)." -msgstr "" +msgstr "%s — недопуÑтимый путь. Ðужен реÑурÑный путь (res://)." #: editor/editor_autoload_settings.cpp msgid "Add AutoLoad" @@ -1641,9 +1649,8 @@ msgid "Name" msgstr "Ðазвание" #: editor/editor_autoload_settings.cpp -#, fuzzy msgid "Global Variable" -msgstr "ПеременнаÑ" +msgstr "Ð“Ð»Ð¾Ð±Ð°Ð»ÑŒÐ½Ð°Ñ Ð¿ÐµÑ€ÐµÐ¼ÐµÐ½Ð½Ð°Ñ" #: editor/editor_data.cpp msgid "Paste Params" @@ -1817,48 +1824,52 @@ msgstr "Панель «Импорт»" #: editor/editor_feature_profile.cpp msgid "Allows to view and edit 3D scenes." -msgstr "" +msgstr "ПозволÑет проÑматривать и редактировать 3D-Ñцены." #: editor/editor_feature_profile.cpp msgid "Allows to edit scripts using the integrated script editor." msgstr "" +"ПозволÑет редактировать Ñкрипты Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ вÑтроенного редактора Ñкриптов." #: editor/editor_feature_profile.cpp msgid "Provides built-in access to the Asset Library." -msgstr "" +msgstr "ПредоÑтавлÑет вÑтроенный доÑтуп к Библиотеке реÑурÑов." #: editor/editor_feature_profile.cpp msgid "Allows editing the node hierarchy in the Scene dock." -msgstr "" +msgstr "ПозволÑет редактировать иерархию узлов в панели «Сцена»." #: editor/editor_feature_profile.cpp msgid "" "Allows to work with signals and groups of the node selected in the Scene " "dock." msgstr "" +"ПозволÑет работать Ñ Ñигналами и группами узла, выбранного в панели «Сцена»." #: editor/editor_feature_profile.cpp msgid "Allows to browse the local file system via a dedicated dock." msgstr "" +"ПозволÑет проÑматривать локальную файловую ÑиÑтему через Ñпециальную панель." #: editor/editor_feature_profile.cpp msgid "" "Allows to configure import settings for individual assets. Requires the " "FileSystem dock to function." msgstr "" +"ПозволÑет наÑтраивать параметры импорта Ð´Ð»Ñ Ð¾Ñ‚Ð´ÐµÐ»ÑŒÐ½Ñ‹Ñ… реÑурÑов. Ð”Ð»Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ñ‹ " +"требуетÑÑ Ð¿Ð°Ð½ÐµÐ»ÑŒ Â«Ð¤Ð°Ð¹Ð»Ð¾Ð²Ð°Ñ ÑиÑтема»." #: editor/editor_feature_profile.cpp -#, fuzzy msgid "(current)" -msgstr "(Текущий)" +msgstr "(текущий)" #: editor/editor_feature_profile.cpp msgid "(none)" -msgstr "" +msgstr "(нет)" #: editor/editor_feature_profile.cpp msgid "Remove currently selected profile, '%s'? Cannot be undone." -msgstr "" +msgstr "Удалить текущий выбранный профиль, «%s»? Ðе может быть отменено." #: editor/editor_feature_profile.cpp msgid "Profile must be a valid filename and must not contain '.'" @@ -1890,19 +1901,16 @@ msgid "Enable Contextual Editor" msgstr "Включить контекÑтный редактор" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Class Properties:" -msgstr "СвойÑтва:" +msgstr "СвойÑтва клаÑÑа:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Main Features:" -msgstr "ВозможноÑти" +msgstr "ОÑновные возможноÑти:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Nodes and Classes:" -msgstr "ДоÑтупные клаÑÑÑ‹:" +msgstr "Узлы и клаÑÑÑ‹:" #: editor/editor_feature_profile.cpp msgid "File '%s' format is invalid, import aborted." @@ -1921,7 +1929,6 @@ msgid "Error saving profile to path: '%s'." msgstr "Ошибка ÑÐ¾Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¾Ñ„Ð¸Ð»Ñ Ð² «%s»." #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Reset to Default" msgstr "СброÑить наÑтройки" @@ -1930,14 +1937,12 @@ msgid "Current Profile:" msgstr "Текущий профиль:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Create Profile" -msgstr "Стереть профиль" +msgstr "Создать профиль" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Remove Profile" -msgstr "Удалить тайл" +msgstr "Удалить профиль" #: editor/editor_feature_profile.cpp msgid "Available Profiles:" @@ -1957,18 +1962,18 @@ msgid "Export" msgstr "ÐкÑпорт" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Configure Selected Profile:" -msgstr "Текущий профиль:" +msgstr "ÐаÑтроить выбранный профиль:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Extra Options:" -msgstr "Параметры текÑтуры" +msgstr "Дополнительные параметры:" #: editor/editor_feature_profile.cpp msgid "Create or import a profile to edit available classes and properties." msgstr "" +"Создайте или импортируйте профиль Ð´Ð»Ñ Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð´Ð¾Ñтупных клаÑÑов и " +"ÑвойÑтв." #: editor/editor_feature_profile.cpp msgid "New profile name:" @@ -1995,7 +2000,6 @@ msgid "Select Current Folder" msgstr "Выбрать текущую папку" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp -#, fuzzy msgid "File exists, overwrite?" msgstr "Файл ÑущеÑтвует, перезапиÑать?" @@ -2390,6 +2394,17 @@ msgid "New Window" msgstr "Ðовое окно" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "ВращаетÑÑ, когда окно редактора перериÑовываетÑÑ." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Импортированные реÑурÑÑ‹ не могут быть Ñохранены." @@ -2621,13 +2636,16 @@ msgid "" "The current scene has no root node, but %d modified external resource(s) " "were saved anyway." msgstr "" +"Ð¢ÐµÐºÑƒÑ‰Ð°Ñ Ñцена не имеет корневого узла, но %d изменённых внешних реÑурÑов вÑÑ‘ " +"равно были Ñохранены." #: editor/editor_node.cpp -#, fuzzy msgid "" "A root node is required to save the scene. You can add a root node using the " "Scene tree dock." -msgstr "Ð”Ð»Ñ ÑÐ¾Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ñцены требуетÑÑ ÐºÐ¾Ñ€Ð½ÐµÐ²Ð¾Ð¹ узел." +msgstr "" +"Ð”Ð»Ñ ÑÐ¾Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ñцены требуетÑÑ ÐºÐ¾Ñ€Ð½ÐµÐ²Ð¾Ð¹ узел. Ð’Ñ‹ можете добавить его, " +"иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÑ Ð¿Ð°Ð½ÐµÐ»ÑŒ «Сцена»." #: editor/editor_node.cpp msgid "Save Scene As..." @@ -3010,9 +3028,8 @@ msgid "Orphan Resource Explorer..." msgstr "Обзор реÑурÑов-Ñирот..." #: editor/editor_node.cpp -#, fuzzy msgid "Reload Current Project" -msgstr "Переименовать проект" +msgstr "Перезагрузить текущий проект" #: editor/editor_node.cpp msgid "Quit to Project List" @@ -3171,22 +3188,20 @@ msgid "Help" msgstr "Справка" #: editor/editor_node.cpp -#, fuzzy msgid "Online Documentation" -msgstr "Открыть документацию" +msgstr "Онлайн-документациÑ" #: editor/editor_node.cpp msgid "Questions & Answers" -msgstr "" +msgstr "ВопроÑÑ‹ и ответы" #: editor/editor_node.cpp msgid "Report a Bug" msgstr "Сообщить об ошибке" #: editor/editor_node.cpp -#, fuzzy msgid "Suggest a Feature" -msgstr "УÑтановить значение" +msgstr "Предложить функцию" #: editor/editor_node.cpp msgid "Send Docs Feedback" @@ -3197,9 +3212,8 @@ msgid "Community" msgstr "СообщеÑтво" #: editor/editor_node.cpp -#, fuzzy msgid "About Godot" -msgstr "О Godot Engine" +msgstr "О Godot" #: editor/editor_node.cpp msgid "Support Godot Development" @@ -3251,10 +3265,6 @@ msgid "Save & Restart" msgstr "Сохранить и перезапуÑтить" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "ВращаетÑÑ, когда окно редактора перериÑовываетÑÑ." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "Ðепрерывное обновление" @@ -3297,14 +3307,12 @@ msgid "Manage Templates" msgstr "Управление шаблонами" #: editor/editor_node.cpp -#, fuzzy msgid "Install from file" msgstr "УÑтановить из файла" #: editor/editor_node.cpp -#, fuzzy msgid "Select android sources file" -msgstr "Выберите иÑточник полиÑетки:" +msgstr "Выберите файл иÑходников android" #: editor/editor_node.cpp msgid "" @@ -3387,9 +3395,8 @@ msgid "Select" msgstr "Выделение" #: editor/editor_node.cpp -#, fuzzy msgid "Select Current" -msgstr "Выбрать текущую папку" +msgstr "Выбрать текущий" #: editor/editor_node.cpp msgid "Open 2D Editor" @@ -3424,9 +3431,8 @@ msgid "No sub-resources found." msgstr "Вложенные реÑурÑÑ‹ не найдены." #: editor/editor_path.cpp -#, fuzzy msgid "Open a list of sub-resources." -msgstr "Вложенные реÑурÑÑ‹ не найдены." +msgstr "Открыть ÑпиÑок вложенных реÑурÑов." #: editor/editor_plugin.cpp msgid "Creating Mesh Previews" @@ -3453,14 +3459,12 @@ msgid "Update" msgstr "Обновление" #: editor/editor_plugin_settings.cpp -#, fuzzy msgid "Version" -msgstr "ВерÑиÑ:" +msgstr "ВерÑиÑ" #: editor/editor_plugin_settings.cpp -#, fuzzy msgid "Author" -msgstr "Ðвторы" +msgstr "Ðвтор" #: editor/editor_plugin_settings.cpp #: editor/plugins/version_control_editor_plugin.cpp @@ -3473,14 +3477,12 @@ msgid "Measure:" msgstr "Единица измерениÑ:" #: editor/editor_profiler.cpp -#, fuzzy msgid "Frame Time (ms)" -msgstr "Ð’Ñ€ÐµÐ¼Ñ ÐºÐ°Ð´Ñ€Ð° (Ñек.)" +msgstr "Ð’Ñ€ÐµÐ¼Ñ ÐºÐ°Ð´Ñ€Ð° (мÑ)" #: editor/editor_profiler.cpp -#, fuzzy msgid "Average Time (ms)" -msgstr "Среднее Ð²Ñ€ÐµÐ¼Ñ (Ñек.)" +msgstr "Среднее Ð²Ñ€ÐµÐ¼Ñ (мÑ)" #: editor/editor_profiler.cpp msgid "Frame %" @@ -3507,6 +3509,12 @@ msgid "" "functions called by that function.\n" "Use this to find individual functions to optimize." msgstr "" +"Включительно: Включает Ð²Ñ€ÐµÐ¼Ñ Ð´Ñ€ÑƒÐ³Ð¸Ñ… функций, вызываемых Ñтой функцией.\n" +"ИÑпользуйте Ð´Ð»Ñ Ð²Ñ‹ÑÐ²Ð»ÐµÐ½Ð¸Ñ ÑƒÐ·ÐºÐ¸Ñ… меÑÑ‚.\n" +"\n" +"Субъект: учитывает только времÑ, затраченное в Ñамой функции, а не в других " +"функциÑÑ…, вызываемых Ñтой функцией.\n" +"ИÑпользуйте Ð´Ð»Ñ Ð¿Ð¾Ð¸Ñка отдельных функций Ð´Ð»Ñ Ð¾Ð¿Ñ‚Ð¸Ð¼Ð¸Ð·Ð°Ñ†Ð¸Ð¸." #: editor/editor_profiler.cpp msgid "Frame #:" @@ -3628,7 +3636,6 @@ msgid "Paste" msgstr "Ð’Ñтавить" #: editor/editor_resource_picker.cpp editor/property_editor.cpp -#, fuzzy msgid "Convert to %s" msgstr "Преобразовать в %s" @@ -3679,10 +3686,9 @@ msgid "Did you forget the '_run' method?" msgstr "Быть может вы забыли метод _run()?" #: editor/editor_spin_slider.cpp -#, fuzzy msgid "Hold %s to round to integers. Hold Shift for more precise changes." msgstr "" -"Зажмите Ctrl, чтобы округлить до целых. Зажмите Shift Ð´Ð»Ñ Ð±Ð¾Ð»ÐµÐµ точных " +"Зажмите %s, чтобы округлить до целых. Зажмите Shift Ð´Ð»Ñ Ð±Ð¾Ð»ÐµÐµ точных " "изменений." #: editor/editor_sub_scene.cpp @@ -3703,49 +3709,43 @@ msgstr "Импортировать из узла:" #: editor/export_template_manager.cpp msgid "Open the folder containing these templates." -msgstr "" +msgstr "Открыть папку, Ñодержащую Ñти шаблоны." #: editor/export_template_manager.cpp msgid "Uninstall these templates." -msgstr "" +msgstr "Удалить Ñти шаблоны." #: editor/export_template_manager.cpp -#, fuzzy msgid "There are no mirrors available." -msgstr "Файла «%s» не ÑущеÑтвует." +msgstr "Ðет доÑтупных зеркал." #: editor/export_template_manager.cpp -#, fuzzy msgid "Retrieving the mirror list..." -msgstr "Получение зеркал, пожалуйÑта, ждите..." +msgstr "Получение ÑпиÑка зеркал..." #: editor/export_template_manager.cpp msgid "Starting the download..." -msgstr "" +msgstr "Ðачало загрузки..." #: editor/export_template_manager.cpp msgid "Error requesting URL:" msgstr "Ошибка при запроÑе URL:" #: editor/export_template_manager.cpp -#, fuzzy msgid "Connecting to the mirror..." msgstr "Подключение к зеркалу..." #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't resolve the requested address." -msgstr "Ðевозможно определить Ð¸Ð¼Ñ Ñ…Ð¾Ñта:" +msgstr "Ðе удалоÑÑŒ разрешить запрошенный адреÑ." #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't connect to the mirror." -msgstr "Ðе удаётÑÑ Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡Ð¸Ñ‚ÑŒÑÑ Ðº хоÑту:" +msgstr "Ðе удалоÑÑŒ подключитьÑÑ Ðº зеркалу." #: editor/export_template_manager.cpp -#, fuzzy msgid "No response from the mirror." -msgstr "Ðет ответа от хоÑта:" +msgstr "Ðет ответа от зеркала." #: editor/export_template_manager.cpp #: editor/plugins/asset_library_editor_plugin.cpp @@ -3753,18 +3753,16 @@ msgid "Request failed." msgstr "Ðе удалоÑÑŒ выполнить запроÑ." #: editor/export_template_manager.cpp -#, fuzzy msgid "Request ended up in a redirect loop." -msgstr "Ð—Ð°Ð¿Ñ€Ð¾Ñ Ð½Ðµ прошёл, Ñлишком много перенаправлений" +msgstr "Ð—Ð°Ð¿Ñ€Ð¾Ñ Ð·Ð°Ð²ÐµÑ€ÑˆÐ¸Ð»ÑÑ Ð¸Ð·-за цикличеÑких перенаправлений." #: editor/export_template_manager.cpp -#, fuzzy msgid "Request failed:" -msgstr "Ðе удалоÑÑŒ выполнить запроÑ." +msgstr "Ðе удалоÑÑŒ выполнить запроÑ:" #: editor/export_template_manager.cpp msgid "Download complete; extracting templates..." -msgstr "" +msgstr "Загрузка завершена; извлечение шаблонов..." #: editor/export_template_manager.cpp msgid "Cannot remove temporary file:" @@ -3783,14 +3781,14 @@ msgid "Error getting the list of mirrors." msgstr "Ошибка при получении ÑпиÑка зеркал." #: editor/export_template_manager.cpp -#, fuzzy msgid "Error parsing JSON with the list of mirrors. Please report this issue!" msgstr "" -"Ошибка парÑинга JSON ÑпиÑка зеркал. ПожалуйÑта, Ñообщите об Ñтой проблеме!" +"Ошибка при разборе JSON Ñо ÑпиÑком зеркал. ПожалуйÑта, Ñообщите об Ñтой " +"проблеме!" #: editor/export_template_manager.cpp msgid "Best available mirror" -msgstr "" +msgstr "Лучшее доÑтупное зеркало" #: editor/export_template_manager.cpp msgid "" @@ -3843,24 +3841,20 @@ msgid "SSL Handshake Error" msgstr "Ошибка Ñ€ÑƒÐºÐ¾Ð¿Ð¾Ð¶Ð°Ñ‚Ð¸Ñ SSH" #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't open the export templates file." -msgstr "Ðе удаётÑÑ Ð¾Ñ‚ÐºÑ€Ñ‹Ñ‚ÑŒ архив шаблонов ÑкÑпорта." +msgstr "Ðе удалоÑÑŒ открыть архив шаблонов ÑкÑпорта." #: editor/export_template_manager.cpp -#, fuzzy msgid "Invalid version.txt format inside the export templates file: %s." -msgstr "Ðеверный формат version.txt файла внутри шаблонов: %s." +msgstr "Ðеверный формат version.txt внутри файла шаблонов ÑкÑпорта: %s." #: editor/export_template_manager.cpp -#, fuzzy msgid "No version.txt found inside the export templates file." -msgstr "Файл version.txt не найден в шаблонах." +msgstr "Файл version.txt не найден внутри файла шаблонов ÑкÑпорта." #: editor/export_template_manager.cpp -#, fuzzy msgid "Error creating path for extracting templates:" -msgstr "Ошибка ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð¿ÑƒÑ‚Ð¸ Ð´Ð»Ñ ÑˆÐ°Ð±Ð»Ð¾Ð½Ð¾Ð²:" +msgstr "Ошибка при Ñоздании пути Ð´Ð»Ñ Ð¸Ð·Ð²Ð»ÐµÑ‡ÐµÐ½Ð¸Ñ ÑˆÐ°Ð±Ð»Ð¾Ð½Ð¾Ð²:" #: editor/export_template_manager.cpp msgid "Extracting Export Templates" @@ -3871,9 +3865,8 @@ msgid "Importing:" msgstr "ИмпортируетÑÑ:" #: editor/export_template_manager.cpp -#, fuzzy msgid "Remove templates for the version '%s'?" -msgstr "Удалить верÑию шаблона «%s»?" +msgstr "Удалить шаблоны Ð´Ð»Ñ Ð²ÐµÑ€Ñии «%s»?" #: editor/export_template_manager.cpp msgid "Uncompressing Android Build Sources" @@ -3889,58 +3882,65 @@ msgstr "Ð¢ÐµÐºÑƒÑ‰Ð°Ñ Ð²ÐµÑ€ÑиÑ:" #: editor/export_template_manager.cpp msgid "Export templates are missing. Download them or install from a file." -msgstr "" +msgstr "Шаблоны ÑкÑпорта отÑутÑтвуют. Загрузите их или уÑтановите из файла." #: editor/export_template_manager.cpp msgid "Export templates are installed and ready to be used." -msgstr "" +msgstr "Шаблоны ÑкÑпорта уÑтановлены и готовы к иÑпользованию." #: editor/export_template_manager.cpp -#, fuzzy msgid "Open Folder" -msgstr "Открыть файл" +msgstr "Открыть папку" #: editor/export_template_manager.cpp msgid "Open the folder containing installed templates for the current version." -msgstr "" +msgstr "Открывает папку, Ñодержащую уÑтановленные шаблоны Ð´Ð»Ñ Ñ‚ÐµÐºÑƒÑ‰ÐµÐ¹ верÑии." #: editor/export_template_manager.cpp msgid "Uninstall" msgstr "Удалить" #: editor/export_template_manager.cpp -#, fuzzy msgid "Uninstall templates for the current version." -msgstr "Ðачальное значение Ð´Ð»Ñ Ñчетчика" +msgstr "Удалить шаблоны Ð´Ð»Ñ Ñ‚ÐµÐºÑƒÑ‰ÐµÐ¹ верÑии." #: editor/export_template_manager.cpp -#, fuzzy msgid "Download from:" -msgstr "Ошибка загрузки" +msgstr "Загрузить из:" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "ЗапуÑтить в браузере" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "Копировать ошибку" #: editor/export_template_manager.cpp msgid "Download and Install" -msgstr "" +msgstr "Загрузить и уÑтановить" #: editor/export_template_manager.cpp msgid "" "Download and install templates for the current version from the best " "possible mirror." msgstr "" +"Загружает и уÑтанавливает шаблоны Ð´Ð»Ñ Ñ‚ÐµÐºÑƒÑ‰ÐµÐ¹ верÑии Ñ Ð»ÑƒÑ‡ÑˆÐµÐ³Ð¾ из доÑтупных " +"зеркал." #: editor/export_template_manager.cpp msgid "Official export templates aren't available for development builds." msgstr "Официальные шаблоны ÑкÑпорта недоÑтупны Ð´Ð»Ñ Ñ€Ð°Ð±Ð¾Ñ‡Ð¸Ñ… Ñборок." #: editor/export_template_manager.cpp -#, fuzzy msgid "Install from File" msgstr "УÑтановить из файла" #: editor/export_template_manager.cpp -#, fuzzy msgid "Install templates from a local file." -msgstr "Импортировать шаблоны из ZIP файла" +msgstr "УÑтановить шаблоны из локального файла." #: editor/export_template_manager.cpp editor/find_in_files.cpp #: editor/progress_dialog.cpp scene/gui/dialogs.cpp @@ -3948,19 +3948,16 @@ msgid "Cancel" msgstr "Отмена" #: editor/export_template_manager.cpp -#, fuzzy msgid "Cancel the download of the templates." -msgstr "Ðе удаётÑÑ Ð¾Ñ‚ÐºÑ€Ñ‹Ñ‚ÑŒ архив шаблонов ÑкÑпорта." +msgstr "Отменить загрузку шаблонов." #: editor/export_template_manager.cpp -#, fuzzy msgid "Other Installed Versions:" -msgstr "УÑтановленные верÑии:" +msgstr "Другие уÑтановленные верÑии:" #: editor/export_template_manager.cpp -#, fuzzy msgid "Uninstall Template" -msgstr "Удалить" +msgstr "Удалить шаблон" #: editor/export_template_manager.cpp msgid "Select Template File" @@ -3975,6 +3972,9 @@ msgid "" "The templates will continue to download.\n" "You may experience a short editor freeze when they finish." msgstr "" +"Шаблоны будут продолжать загружатьÑÑ.\n" +"ПоÑле Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð¸Ñ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸ может произойти кратковременное завиÑание " +"редактора." #: editor/filesystem_dock.cpp msgid "Favorites" @@ -4122,35 +4122,32 @@ msgid "Collapse All" msgstr "Свернуть вÑе" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort files" -msgstr "ПоиÑк файлов" +msgstr "Сортировать файлы" #: editor/filesystem_dock.cpp msgid "Sort by Name (Ascending)" -msgstr "" +msgstr "Сортировать по имени (по возраÑтанию)" #: editor/filesystem_dock.cpp msgid "Sort by Name (Descending)" -msgstr "" +msgstr "Сортировать по имени (по убыванию)" #: editor/filesystem_dock.cpp msgid "Sort by Type (Ascending)" -msgstr "" +msgstr "Сортировать по типу (по возраÑтанию)" #: editor/filesystem_dock.cpp msgid "Sort by Type (Descending)" -msgstr "" +msgstr "Сортировать по типу (по убыванию)" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort by Last Modified" -msgstr "ПоÑледнее изменение" +msgstr "Сортировать по поÑледнему изменению" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort by First Modified" -msgstr "ПоÑледнее изменение" +msgstr "Сортировать по первому изменению" #: editor/filesystem_dock.cpp msgid "Duplicate..." @@ -4162,7 +4159,7 @@ msgstr "Переименовать..." #: editor/filesystem_dock.cpp msgid "Focus the search box" -msgstr "" +msgstr "СфокуÑироватьÑÑ Ð½Ð° поле поиÑка" #: editor/filesystem_dock.cpp msgid "Previous Folder/File" @@ -4470,14 +4467,12 @@ msgid "Failed to load resource." msgstr "Ðе удалоÑÑŒ загрузить реÑурÑ." #: editor/inspector_dock.cpp -#, fuzzy msgid "Copy Properties" -msgstr "СвойÑтва" +msgstr "Копировать ÑвойÑтва" #: editor/inspector_dock.cpp -#, fuzzy msgid "Paste Properties" -msgstr "СвойÑтва" +msgstr "Ð’Ñтавить ÑвойÑтва" #: editor/inspector_dock.cpp msgid "Make Sub-Resources Unique" @@ -4502,23 +4497,20 @@ msgid "Save As..." msgstr "Сохранить как..." #: editor/inspector_dock.cpp -#, fuzzy msgid "Extra resource options." -msgstr "Ðе в пути реÑурÑов." +msgstr "Дополнительные параметры реÑурÑа." #: editor/inspector_dock.cpp -#, fuzzy msgid "Edit Resource from Clipboard" -msgstr "Редактировать реÑÑƒÑ€Ñ Ð² буфере обмена" +msgstr "Редактировать реÑÑƒÑ€Ñ Ð¸Ð· буфера обмена" #: editor/inspector_dock.cpp msgid "Copy Resource" msgstr "Копировать параметры" #: editor/inspector_dock.cpp -#, fuzzy msgid "Make Resource Built-In" -msgstr "Сделать вÑтроенным" +msgstr "Сделать реÑÑƒÑ€Ñ Ð²Ñтроенным" #: editor/inspector_dock.cpp msgid "Go to the previous edited object in history." @@ -4533,9 +4525,8 @@ msgid "History of recently edited objects." msgstr "ИÑÑ‚Ð¾Ñ€Ð¸Ñ Ð½ÐµÐ´Ð°Ð²Ð½Ð¾ отредактированных объектов." #: editor/inspector_dock.cpp -#, fuzzy msgid "Open documentation for this object." -msgstr "Открыть документацию" +msgstr "Открыть документацию Ð´Ð»Ñ Ñтого объекта." #: editor/inspector_dock.cpp editor/scene_tree_dock.cpp msgid "Open Documentation" @@ -4546,9 +4537,8 @@ msgid "Filter properties" msgstr "Фильтр ÑвойÑтв" #: editor/inspector_dock.cpp -#, fuzzy msgid "Manage object properties." -msgstr "СвойÑтва объекта." +msgstr "Управление ÑвойÑтвами объекта." #: editor/inspector_dock.cpp msgid "Changes may be lost!" @@ -4793,9 +4783,8 @@ msgid "Blend:" msgstr "Смешивание:" #: editor/plugins/animation_blend_tree_editor_plugin.cpp -#, fuzzy msgid "Parameter Changed:" -msgstr "Параметр изменён" +msgstr "Параметр изменён:" #: editor/plugins/animation_blend_tree_editor_plugin.cpp #: editor/plugins/animation_tree_player_editor_plugin.cpp @@ -5525,11 +5514,11 @@ msgstr "Ð’Ñе" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Search templates, projects, and demos" -msgstr "" +msgstr "ПоиÑк шаблонов, проектов и демо" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Search assets (excluding templates, projects, and demos)" -msgstr "" +msgstr "ПоиÑк реÑурÑов (иÑÐºÐ»ÑŽÑ‡Ð°Ñ ÑˆÐ°Ð±Ð»Ð¾Ð½Ñ‹, проекты и демо)" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Import..." @@ -5573,7 +5562,7 @@ msgstr "ZIP файл аÑÑетов" #: editor/plugins/audio_stream_editor_plugin.cpp msgid "Audio Preview Play/Pause" -msgstr "" +msgstr "Ðудио превью Старт/Пауза" #: editor/plugins/baked_lightmap_editor_plugin.cpp msgid "" @@ -5830,13 +5819,12 @@ msgstr "Изменить привÑзку" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "" "Project Camera Override\n" "Overrides the running project's camera with the editor viewport camera." msgstr "" "Переопределение игровой камеры\n" -"ПереопределÑет игровую камеру камерой редактора viewport." +"ПереопределÑет игровую камеру запущенного проекта камерой viewport редактора." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp @@ -5845,6 +5833,9 @@ msgid "" "No project instance running. Run the project from the editor to use this " "feature." msgstr "" +"Переопределение игровой камеры\n" +"ÐкземплÑÑ€ проекта не запущен. ЗапуÑтите проект из редактора, чтобы " +"иÑпользовать Ñту функцию." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp @@ -5912,31 +5903,27 @@ msgstr "Режим выделениÑ" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Drag: Rotate selected node around pivot." -msgstr "Удалить выделенный узел или переход." +msgstr "Тащить: Вращать выделенный узел вокруг pivot." #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Alt+Drag: Move selected node." -msgstr "Alt+Тащить: Перемещение" +msgstr "Alt+Тащить: перемещение выбранного узла." #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "V: Set selected node's pivot position." -msgstr "Удалить выделенный узел или переход." +msgstr "V: Задать положение pivot выделенного узла." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Alt+RMB: Show list of all nodes at position clicked, including locked." msgstr "" -"Показывает ÑпиÑок вÑех объектов нажатой позиции,\n" -"(так же как и Alt+ПКМ в режиме выделениÑ)." +"Alt+ПКМ: Показать ÑпиÑок вÑех узлов в выбранной позиции, Ð²ÐºÐ»ÑŽÑ‡Ð°Ñ " +"заблокированные." #: editor/plugins/canvas_item_editor_plugin.cpp msgid "RMB: Add node at position clicked." -msgstr "" +msgstr "ПКМ: Добавить узел в выбранной позиции." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp @@ -6174,14 +6161,12 @@ msgid "Clear Pose" msgstr "ОчиÑтить позу" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Add Node Here" -msgstr "Добавить узел" +msgstr "Добавить узел Ñюда" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Instance Scene Here" -msgstr "Дополнить Ñценой(ами)" +msgstr "ИнÑтанцировать Ñцену Ñюда" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Multiply grid step by 2" @@ -6197,49 +6182,43 @@ msgstr "Панорамировать вид" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 3.125%" -msgstr "" +msgstr "МаÑштаб 3,125%" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 6.25%" -msgstr "" +msgstr "МаÑштаб 6.25%" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 12.5%" -msgstr "" +msgstr "МаÑштаб 12.5%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 25%" -msgstr "Отдалить" +msgstr "МаÑштаб 25%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 50%" -msgstr "Отдалить" +msgstr "МаÑштаб 50%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 100%" -msgstr "Отдалить" +msgstr "МаÑштаб 100%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 200%" -msgstr "Отдалить" +msgstr "МаÑштаб 200%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 400%" -msgstr "Отдалить" +msgstr "МаÑштаб 400%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 800%" -msgstr "Отдалить" +msgstr "МаÑштаб 800%" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 1600%" -msgstr "" +msgstr "МаÑштаб 1600%" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Add %s" @@ -6484,9 +6463,8 @@ msgid "Couldn't create a single convex collision shape." msgstr "Ðе удалоÑÑŒ Ñоздать ни одной выпуклой формы ÑтолкновениÑ." #: editor/plugins/mesh_instance_editor_plugin.cpp -#, fuzzy msgid "Create Simplified Convex Shape" -msgstr "Создать одну выпуклую форму" +msgstr "Создать упрощённую выпуклую форму" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Create Single Convex Shape" @@ -6522,9 +6500,8 @@ msgid "No mesh to debug." msgstr "Ðет полиÑетки Ð´Ð»Ñ Ð¾Ñ‚Ð»Ð°Ð´ÐºÐ¸." #: editor/plugins/mesh_instance_editor_plugin.cpp -#, fuzzy msgid "Mesh has no UV in layer %d." -msgstr "У модели нет UV в Ñтом Ñлое" +msgstr "ПолиÑетка не имеет UV в Ñлое %d." #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "MeshInstance lacks a Mesh!" @@ -6589,9 +6566,8 @@ msgstr "" "Ðто Ñамый быÑтрый (но наименее точный) ÑпоÑоб Ð¾Ð±Ð½Ð°Ñ€ÑƒÐ¶ÐµÐ½Ð¸Ñ Ñтолкновений." #: editor/plugins/mesh_instance_editor_plugin.cpp -#, fuzzy msgid "Create Simplified Convex Collision Sibling" -msgstr "Создать одну выпуклую облаÑть ÑтолкновениÑ" +msgstr "Создать ÑоÑеднюю упрощённую выпуклую коллизию" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "" @@ -6599,20 +6575,23 @@ msgid "" "This is similar to single collision shape, but can result in a simpler " "geometry in some cases, at the cost of accuracy." msgstr "" +"Создает упрощенную выпуклую форму ÑтолкновениÑ.\n" +"Она похожа на одиночную форму ÑтолкновениÑ, но в некоторых ÑлучаÑÑ… может " +"получитьÑÑ Ð±Ð¾Ð»ÐµÐµ проÑÑ‚Ð°Ñ Ð³ÐµÐ¾Ð¼ÐµÑ‚Ñ€Ð¸Ñ, ценой ÑÐ½Ð¸Ð¶ÐµÐ½Ð¸Ñ Ñ‚Ð¾Ñ‡Ð½Ð¾Ñти." #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Create Multiple Convex Collision Siblings" msgstr "Создать выпуклую облаÑть ÑтолкновениÑ" #: editor/plugins/mesh_instance_editor_plugin.cpp -#, fuzzy msgid "" "Creates a polygon-based collision shape.\n" "This is a performance middle-ground between a single convex collision and a " "polygon-based collision." msgstr "" "Создает форму ÑÑ‚Ð¾Ð»ÐºÐ½Ð¾Ð²ÐµÐ½Ð¸Ñ Ð½Ð° оÑнове полигона.\n" -"Ðто Ñредний по производительноÑти вариант между Ð´Ð²ÑƒÐ¼Ñ Ð¿Ñ€ÐµÐ´Ñ‹Ð´ÑƒÑ‰Ð¸Ð¼Ð¸." +"Ðто Ñредний по производительноÑти вариант между одиночным выпуклым " +"Ñтолкновением и Ñтолкновением на оÑнове полигонов." #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Create Outline Mesh..." @@ -7255,24 +7234,20 @@ msgid "ResourcePreloader" msgstr "Предзагрузчик реÑурÑов" #: editor/plugins/room_manager_editor_plugin.cpp -#, fuzzy msgid "Flip Portals" -msgstr "Перевернуть по горизонтали" +msgstr "Перевернуть порталы" #: editor/plugins/room_manager_editor_plugin.cpp -#, fuzzy msgid "Room Generate Points" -msgstr "КоличеÑтво Ñоздаваемых точек:" +msgstr "Точки генерации комнаты" #: editor/plugins/room_manager_editor_plugin.cpp -#, fuzzy msgid "Generate Points" -msgstr "КоличеÑтво Ñоздаваемых точек:" +msgstr "Генерировать точки" #: editor/plugins/room_manager_editor_plugin.cpp -#, fuzzy msgid "Flip Portal" -msgstr "Перевернуть по горизонтали" +msgstr "Перевернуть портал" #: editor/plugins/root_motion_editor_plugin.cpp msgid "AnimationTree has no path set to an AnimationPlayer" @@ -7836,20 +7811,17 @@ msgid "None" msgstr "ПуÑто" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Rotate" -msgstr "ГоÑударÑтво" +msgstr "Повернуть" #. TRANSLATORS: This refers to the movement that changes the position of an object. #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Translate" -msgstr "Перемещение:" +msgstr "Сдвинуть" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Scale" -msgstr "МаÑштаб:" +msgstr "МаÑштабировать" #: editor/plugins/spatial_editor_plugin.cpp msgid "Scaling: " @@ -7872,52 +7844,44 @@ msgid "Animation Key Inserted." msgstr "Ключ анимации вÑтавлен." #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Pitch:" -msgstr "Ð’Ñ‹Ñота" +msgstr "Ð’Ñ‹Ñота:" #: editor/plugins/spatial_editor_plugin.cpp msgid "Yaw:" -msgstr "" +msgstr "Ð Ñ‹Ñкание:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Size:" -msgstr "Размер: " +msgstr "Размер:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Objects Drawn:" -msgstr "ÐариÑовано объектов" +msgstr "ОтриÑовано объектов:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Material Changes:" -msgstr "Ð˜Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð¼Ð°Ñ‚ÐµÑ€Ð¸Ð°Ð»Ð°" +msgstr "Материалов изменено:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Shader Changes:" -msgstr "Ð˜Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ ÑˆÐµÐ¹Ð´ÐµÑ€Ð¾Ð²" +msgstr "Шейдеров изменено:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Surface Changes:" -msgstr "Ð˜Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð²ÐµÑ€Ñ…Ð½Ð¾Ñти" +msgstr "ПоверхноÑтей изменено:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Draw Calls:" -msgstr "Вызовы отриÑовки" +msgstr "Вызовов отриÑовки:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Vertices:" -msgstr "Вершины" +msgstr "Вершины:" #: editor/plugins/spatial_editor_plugin.cpp msgid "FPS: %d (%s ms)" -msgstr "" +msgstr "FPS: %d (%s мÑ)" #: editor/plugins/spatial_editor_plugin.cpp msgid "Top View." @@ -8072,9 +8036,8 @@ msgid "Freelook Slow Modifier" msgstr "Модификатор Ð·Ð°Ð¼ÐµÐ´Ð»ÐµÐ½Ð¸Ñ Ñвободного вида" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Toggle Camera Preview" -msgstr "Изменить размер камеры" +msgstr "Переключить превью камеры" #: editor/plugins/spatial_editor_plugin.cpp msgid "View Rotation Locked" @@ -8097,9 +8060,8 @@ msgstr "" "игры." #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Convert Rooms" -msgstr "Преобразовать в %s" +msgstr "Преобразовать комнаты" #: editor/plugins/spatial_editor_plugin.cpp msgid "XForm Dialog" @@ -8121,7 +8083,6 @@ msgstr "" "(«рентген»)." #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Snap Nodes to Floor" msgstr "ПривÑзать узлы к полу" @@ -8139,7 +8100,7 @@ msgstr "ИÑпользовать привÑзки" #: editor/plugins/spatial_editor_plugin.cpp msgid "Converts rooms for portal culling." -msgstr "" +msgstr "Преобразует комнаты Ð´Ð»Ñ portal culling." #: editor/plugins/spatial_editor_plugin.cpp msgid "Bottom View" @@ -8235,9 +8196,8 @@ msgid "View Grid" msgstr "Отображать Ñетку" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "View Portal Culling" -msgstr "ÐаÑтройки окна проÑмотра" +msgstr "Отображать portal culling" #: editor/plugins/spatial_editor_plugin.cpp #: modules/gridmap/grid_map_editor_plugin.cpp @@ -8559,221 +8519,196 @@ msgid "TextureRegion" msgstr "ОблаÑть текÑтуры" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Colors" -msgstr "Цвет" +msgstr "Цвета" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Fonts" -msgstr "Шрифт" +msgstr "Шрифты" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Icons" -msgstr "Иконка" +msgstr "Иконки" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Styleboxes" -msgstr "Стиль" +msgstr "Стили" #: editor/plugins/theme_editor_plugin.cpp msgid "{num} color(s)" -msgstr "" +msgstr "{num} цвет(ов)" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No colors found." -msgstr "Вложенные реÑурÑÑ‹ не найдены." +msgstr "Цвета не найдены." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "{num} constant(s)" -msgstr "КонÑтанты" +msgstr "{num} конÑтанта(Ñ‹)" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No constants found." -msgstr "Ð¦Ð²ÐµÑ‚Ð¾Ð²Ð°Ñ ÐºÐ¾Ð½Ñтанта." +msgstr "КонÑтанты не найдены." #: editor/plugins/theme_editor_plugin.cpp msgid "{num} font(s)" -msgstr "" +msgstr "{num} шрифт(ов)" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No fonts found." -msgstr "Ðе найдено!" +msgstr "Шрифты не найдены." #: editor/plugins/theme_editor_plugin.cpp msgid "{num} icon(s)" -msgstr "" +msgstr "{num} иконка(ок)" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No icons found." -msgstr "Ðе найдено!" +msgstr "Иконки не найдены." #: editor/plugins/theme_editor_plugin.cpp msgid "{num} stylebox(es)" -msgstr "" +msgstr "{num} Ñтиль(ей)" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No styleboxes found." -msgstr "Вложенные реÑурÑÑ‹ не найдены." +msgstr "Стили не найдены." #: editor/plugins/theme_editor_plugin.cpp msgid "{num} currently selected" -msgstr "" +msgstr "{num} выбрано в данный момент" #: editor/plugins/theme_editor_plugin.cpp msgid "Nothing was selected for the import." -msgstr "" +msgstr "Ð”Ð»Ñ Ð¸Ð¼Ð¿Ð¾Ñ€Ñ‚Ð° ничего не выбрано." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Importing Theme Items" -msgstr "Импортировать тему" +msgstr "Импортировать Ñлементы темы" #: editor/plugins/theme_editor_plugin.cpp msgid "Importing items {n}/{n}" -msgstr "" +msgstr "Импорт Ñлементов {n}/{n}" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Updating the editor" -msgstr "Выйти из редактора?" +msgstr "Обновление редактора" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Finalizing" -msgstr "Ðнализ" +msgstr "Завершение" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Filter:" -msgstr "Фильтр: " +msgstr "Фильтр:" #: editor/plugins/theme_editor_plugin.cpp msgid "With Data" -msgstr "" +msgstr "С данными" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select by data type:" -msgstr "Выбрать узел" +msgstr "Выбрать по типу данных:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible color items." -msgstr "Выберите разделение, чтобы Ñтереть его." +msgstr "Выбрать вÑе видимые цвета." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible color items and their data." -msgstr "" +msgstr "Выделить вÑе видимые цвета и их данные." #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all visible color items." -msgstr "" +msgstr "СнÑть выделение Ñо вÑех видимых цветов." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible constant items." -msgstr "Сначала выберите Ñлемент наÑтроек!" +msgstr "Выбрать вÑе видимые конÑтанты." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible constant items and their data." -msgstr "" +msgstr "Выделить вÑе видимые конÑтанты и их данные." #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all visible constant items." -msgstr "" +msgstr "СнÑть выделение Ñо вÑех видимых конÑтант." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible font items." -msgstr "Сначала выберите Ñлемент наÑтроек!" +msgstr "Выбрать вÑе видимые шрифты." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible font items and their data." -msgstr "" +msgstr "Выделить вÑе видимые шрифты и их данные." #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all visible font items." -msgstr "" +msgstr "СнÑть выделение Ñо вÑех видимых шрифтов." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible icon items." -msgstr "Сначала выберите Ñлемент наÑтроек!" +msgstr "Выбрать вÑе видимые иконки." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible icon items and their data." -msgstr "Сначала выберите Ñлемент наÑтроек!" +msgstr "Выбрать вÑе видимые иконки и их данные." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Deselect all visible icon items." -msgstr "Сначала выберите Ñлемент наÑтроек!" +msgstr "СнÑть выделение Ñо вÑех видимых иконок." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible stylebox items." -msgstr "" +msgstr "Выделить вÑе видимые Ñтили." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible stylebox items and their data." -msgstr "" +msgstr "Выделить вÑе видимые Ñтили и их данные." #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all visible stylebox items." -msgstr "" +msgstr "СнÑть выделение Ñо вÑех видимых Ñтилей." #: editor/plugins/theme_editor_plugin.cpp msgid "" "Caution: Adding icon data may considerably increase the size of your Theme " "resource." msgstr "" +"Внимание: Добавление данных об иконках может значительно увеличить размер " +"реÑурÑа вашей темы." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Collapse types." -msgstr "Свернуть вÑе" +msgstr "Свернуть типы." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Expand types." -msgstr "Развернуть вÑе" +msgstr "Развернуть типы." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all Theme items." -msgstr "Выбрать файл шаблона" +msgstr "Выбрать вÑе Ñлементы темы." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select With Data" -msgstr "Выбрать точки" +msgstr "Выделить Ñ Ð´Ð°Ð½Ð½Ñ‹Ð¼Ð¸" #: editor/plugins/theme_editor_plugin.cpp msgid "Select all Theme items with item data." -msgstr "" +msgstr "Выделить вÑе Ñлементы темы Ñ Ð¸Ñ… данными." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Deselect All" -msgstr "Выделить вÑÑ‘" +msgstr "СнÑть выделение Ñо вÑего" #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all Theme items." -msgstr "" +msgstr "СнÑть выделение Ñо вÑех Ñлементов темы." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Import Selected" -msgstr "Импортировать Ñцену" +msgstr "Импортировать выделенное" #: editor/plugins/theme_editor_plugin.cpp msgid "" @@ -8781,271 +8716,248 @@ msgid "" "closing this window.\n" "Close anyway?" msgstr "" +"Ðа вкладке «Импорт Ñлементов» выбраны некоторые Ñлементы. При закрытии Ñтого " +"окна выбор будет потерÑн.\n" +"Ð’ÑÑ‘ равно закрыть?" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All Color Items" -msgstr "Удалить вÑе Ñлементы" +msgstr "Удалить вÑе цвета" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Item" -msgstr "Удалить Ñлемент" +msgstr "Переименовать Ñлемент" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All Constant Items" -msgstr "Удалить вÑе Ñлементы" +msgstr "Удалить вÑе конÑтанты" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All Font Items" -msgstr "Удалить вÑе Ñлементы" +msgstr "Удалить вÑе шрифты" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All Icon Items" -msgstr "Удалить вÑе Ñлементы" +msgstr "Удалить вÑе иконки" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All StyleBox Items" -msgstr "Удалить вÑе Ñлементы" +msgstr "Удалить вÑе Ñтили" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Color Item" -msgstr "Добавить Ñлемент клаÑÑа" +msgstr "Добавить цвет" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Constant Item" -msgstr "Добавить Ñлемент клаÑÑа" +msgstr "Добавить конÑтанту" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Font Item" -msgstr "Добавить Ñлемент" +msgstr "Добавить шрифт" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Icon Item" -msgstr "Добавить Ñлемент" +msgstr "Добавить иконку" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Stylebox Item" -msgstr "Добавить вÑе Ñлементы" +msgstr "Добавить Ñтиль" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Color Item" -msgstr "Удалить Ñлемент клаÑÑа" +msgstr "Переименовать цвет" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Constant Item" -msgstr "Удалить Ñлемент клаÑÑа" +msgstr "Переименовать конÑтанту" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Font Item" -msgstr "Переименовать узел" +msgstr "Переименовать шрифт" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Icon Item" -msgstr "Переименовать узел" +msgstr "Переименовать иконку" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Stylebox Item" -msgstr "Удалить выбранный Ñлемент" +msgstr "Удалить Ñтиль" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Invalid file, not a Theme resource." -msgstr "ÐедопуÑтимый файл, не раÑкладка аудио шины." +msgstr "Ðеверный файл, не реÑÑƒÑ€Ñ Ñ‚ÐµÐ¼Ñ‹." #: editor/plugins/theme_editor_plugin.cpp msgid "Invalid file, same as the edited Theme resource." -msgstr "" +msgstr "ÐедопуÑтимый файл, так же, как и редактируемый реÑÑƒÑ€Ñ Ñ‚ÐµÐ¼Ñ‹." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Manage Theme Items" -msgstr "Управление шаблонами" +msgstr "Управление Ñлементами темы" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Edit Items" -msgstr "Редактируемый Ñлемент" +msgstr "Редактировать Ñлементы" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Types:" -msgstr "Тип:" +msgstr "Типы:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Type:" -msgstr "Тип:" +msgstr "Добавить тип:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Item:" -msgstr "Добавить Ñлемент" +msgstr "Добавить Ñлемент:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add StyleBox Item" -msgstr "Добавить вÑе Ñлементы" +msgstr "Добавить Ñтиль" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove Items:" -msgstr "Удалить Ñлемент" +msgstr "Удалить Ñлементы:" #: editor/plugins/theme_editor_plugin.cpp msgid "Remove Class Items" msgstr "Удалить Ñлемент клаÑÑа" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove Custom Items" -msgstr "Удалить Ñлемент клаÑÑа" +msgstr "Удалить пользовательÑкие Ñлементы" #: editor/plugins/theme_editor_plugin.cpp msgid "Remove All Items" msgstr "Удалить вÑе Ñлементы" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Theme Item" -msgstr "Тема Ñлементов GUI" +msgstr "Добавить Ñлемент темы" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Old Name:" -msgstr "Ð˜Ð¼Ñ ÑƒÐ·Ð»Ð°:" +msgstr "Старое имÑ:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Import Items" -msgstr "Импортировать тему" +msgstr "Импортировать Ñлементы" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Default Theme" -msgstr "По умолчанию" +msgstr "Тема по умолчанию" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Editor Theme" -msgstr "Редактировать тему" +msgstr "Тема редактора" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select Another Theme Resource:" -msgstr "Удалить реÑурÑ" +msgstr "Выбрать другой реÑÑƒÑ€Ñ Ñ‚ÐµÐ¼Ñ‹:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Another Theme" -msgstr "Импортировать тему" +msgstr "Ð”Ñ€ÑƒÐ³Ð°Ñ Ñ‚ÐµÐ¼Ð°" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Confirm Item Rename" -msgstr "Переименовать дорожку" +msgstr "Подтвердить переименование Ñлемента" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Cancel Item Rename" -msgstr "Групповое переименование" +msgstr "Отменить переименование Ñлемента" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Override Item" -msgstr "Переопределить" +msgstr "Переопределить Ñлемент" #: editor/plugins/theme_editor_plugin.cpp msgid "Unpin this StyleBox as a main style." -msgstr "" +msgstr "Открепить Ñтот StyleBox в качеÑтве оÑновного ÑтилÑ." #: editor/plugins/theme_editor_plugin.cpp msgid "" "Pin this StyleBox as a main style. Editing its properties will update the " "same properties in all other StyleBoxes of this type." msgstr "" +"Закрепить Ñтот StyleBox в качеÑтве оÑновного ÑтилÑ. При редактировании его " +"ÑвойÑтв будут обновлены те же ÑвойÑтва во вÑех других объектах StyleBoxes " +"Ñтого типа." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Type" -msgstr "Тип" +msgstr "Добавить тип" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Item Type" -msgstr "Добавить Ñлемент" +msgstr "Добавить тип Ñлемента" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Node Types:" -msgstr "Тип узла" +msgstr "Типы узлов:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Show Default" -msgstr "Загрузить по умолчанию" +msgstr "Показать по умолчанию" #: editor/plugins/theme_editor_plugin.cpp msgid "Show default type items alongside items that have been overridden." msgstr "" +"Показывать Ñлементы типа по умолчанию Ñ€Ñдом Ñ Ñлементами, которые были " +"переопределены." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Override All" -msgstr "Переопределить" +msgstr "Переопределить вÑÑ‘" #: editor/plugins/theme_editor_plugin.cpp msgid "Override all default type items." -msgstr "" +msgstr "Переопределить вÑе Ñлементы типа по умолчанию." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Theme:" -msgstr "Тема" +msgstr "Тема:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Manage Items..." -msgstr "Управление шаблонами ÑкÑпорта..." +msgstr "Управление Ñлементами..." #: editor/plugins/theme_editor_plugin.cpp msgid "Add, remove, organize and import Theme items." -msgstr "" +msgstr "Добавление, удаление, упорÑдочивание и импорт Ñлементов темы." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Preview" -msgstr "ПредпроÑмотр" +msgstr "Добавить превью" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Default Preview" -msgstr "Обновить предварительный проÑмотр" +msgstr "Превью по умолчанию" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select UI Scene:" -msgstr "Выберите иÑточник полиÑетки:" +msgstr "Выбрать UI-Ñцену:" #: editor/plugins/theme_editor_preview.cpp msgid "" "Toggle the control picker, allowing to visually select control types for " "edit." msgstr "" +"Переключает ÑредÑтво выбора Control, позволÑÑ Ð²Ð¸Ð·ÑƒÐ°Ð»ÑŒÐ½Ð¾ выбирать типы " +"Control Ð´Ð»Ñ Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ." #: editor/plugins/theme_editor_preview.cpp msgid "Toggle Button" @@ -9080,9 +8992,8 @@ msgid "Checked Radio Item" msgstr "Отмеченный переключатель" #: editor/plugins/theme_editor_preview.cpp -#, fuzzy msgid "Named Separator" -msgstr "Имен. раздел." +msgstr "Именованный разделитель" #: editor/plugins/theme_editor_preview.cpp msgid "Submenu" @@ -9135,19 +9046,20 @@ msgstr "ЕÑть,Много,Вариантов" #: editor/plugins/theme_editor_preview.cpp msgid "Invalid path, the PackedScene resource was probably moved or removed." msgstr "" +"ÐедопуÑтимый путь, реÑÑƒÑ€Ñ PackedScene, вероÑтно, был перемещен или удален." #: editor/plugins/theme_editor_preview.cpp msgid "Invalid PackedScene resource, must have a Control node at its root." msgstr "" +"ÐедопуÑтимый реÑÑƒÑ€Ñ PackedScene, он должен иметь корневой узел Control." #: editor/plugins/theme_editor_preview.cpp -#, fuzzy msgid "Invalid file, not a PackedScene resource." -msgstr "ÐедопуÑтимый файл, не раÑкладка аудио шины." +msgstr "Ðеверный файл, не реÑÑƒÑ€Ñ PackedScene." #: editor/plugins/theme_editor_preview.cpp msgid "Reload the scene to reflect its most actual state." -msgstr "" +msgstr "Перезагрузите Ñцену, чтобы отразить её наиболее актуальное ÑоÑтоÑние." #: editor/plugins/tile_map_editor_plugin.cpp msgid "Erase Selection" @@ -10545,9 +10457,8 @@ msgid "VisualShader" msgstr "Визуальный шейдер" #: editor/plugins/visual_shader_editor_plugin.cpp -#, fuzzy msgid "Edit Visual Property:" -msgstr "Редактировать визуальное ÑвойÑтво" +msgstr "Редактировать визуальное ÑвойÑтво:" #: editor/plugins/visual_shader_editor_plugin.cpp msgid "Visual Shader Mode Changed" @@ -10673,9 +10584,8 @@ msgid "Script" msgstr "Скрипт" #: editor/project_export.cpp -#, fuzzy msgid "GDScript Export Mode:" -msgstr "Режим ÑкÑÐ¿Ð¾Ñ€Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ñкриптов:" +msgstr "Режим ÑкÑпорта GDScript:" #: editor/project_export.cpp msgid "Text" @@ -10683,22 +10593,20 @@ msgstr "ТекÑтовый" #: editor/project_export.cpp msgid "Compiled Bytecode (Faster Loading)" -msgstr "" +msgstr "Скомпилированный байткод (более быÑÑ‚Ñ€Ð°Ñ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ°)" #: editor/project_export.cpp msgid "Encrypted (Provide Key Below)" msgstr "Зашифрованный (Ðапишите ключ ниже)" #: editor/project_export.cpp -#, fuzzy msgid "Invalid Encryption Key (must be 64 hexadecimal characters long)" msgstr "" -"ÐедейÑтвительный ключ ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ (длина ключа должна ÑоÑтавлÑть 64 Ñимвола)" +"Ðеверный ключ ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ (должен ÑоÑтоÑть из 64 шеÑтнадцатеричных Ñимволов)" #: editor/project_export.cpp -#, fuzzy msgid "GDScript Encryption Key (256-bits as hexadecimal):" -msgstr "Ключ ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ñкрипта (256-бит, в шеÑтнадцатеричном виде):" +msgstr "Ключ ÑˆÐ¸Ñ„Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ GDScript (256 бит, в шеÑтнадцатеричном виде):" #: editor/project_export.cpp msgid "Export PCK/Zip" @@ -10770,7 +10678,6 @@ msgid "Imported Project" msgstr "Импортированный проект" #: editor/project_manager.cpp -#, fuzzy msgid "Invalid project name." msgstr "ÐедопуÑтимое Ð¸Ð¼Ñ Ð¿Ñ€Ð¾ÐµÐºÑ‚Ð°." @@ -10996,14 +10903,12 @@ msgid "Are you sure to run %d projects at once?" msgstr "Ð’Ñ‹ уверены, что хотите запуÑтить %d проектов одновременно?" #: editor/project_manager.cpp -#, fuzzy msgid "Remove %d projects from the list?" -msgstr "Выберите уÑтройÑтво из ÑпиÑка" +msgstr "Удалить %d проектов из ÑпиÑка?" #: editor/project_manager.cpp -#, fuzzy msgid "Remove this project from the list?" -msgstr "Выберите уÑтройÑтво из ÑпиÑка" +msgstr "Удалить Ñтот проект из ÑпиÑка?" #: editor/project_manager.cpp msgid "" @@ -11035,9 +10940,8 @@ msgid "Project Manager" msgstr "Менеджер проектов" #: editor/project_manager.cpp -#, fuzzy msgid "Local Projects" -msgstr "Проекты" +msgstr "Локальные проекты" #: editor/project_manager.cpp msgid "Loading, please wait..." @@ -11048,23 +10952,20 @@ msgid "Last Modified" msgstr "ПоÑледнее изменение" #: editor/project_manager.cpp -#, fuzzy msgid "Edit Project" -msgstr "ÐкÑпортировать проект" +msgstr "Редактировать проект" #: editor/project_manager.cpp -#, fuzzy msgid "Run Project" -msgstr "Переименовать проект" +msgstr "ЗапуÑтить проект" #: editor/project_manager.cpp msgid "Scan" msgstr "Сканировать" #: editor/project_manager.cpp -#, fuzzy msgid "Scan Projects" -msgstr "Проекты" +msgstr "Сканировать проекты" #: editor/project_manager.cpp msgid "Select a Folder to Scan" @@ -11075,12 +10976,10 @@ msgid "New Project" msgstr "Ðовый проект" #: editor/project_manager.cpp -#, fuzzy msgid "Import Project" -msgstr "Импортированный проект" +msgstr "Импортировать проект" #: editor/project_manager.cpp -#, fuzzy msgid "Remove Project" msgstr "Переименовать проект" @@ -11093,9 +10992,8 @@ msgid "About" msgstr "О Godot Engine" #: editor/project_manager.cpp -#, fuzzy msgid "Asset Library Projects" -msgstr "Библиотека реÑурÑов" +msgstr "Проекты Библиотеки реÑурÑов" #: editor/project_manager.cpp msgid "Restart Now" @@ -11107,7 +11005,7 @@ msgstr "Удалить вÑе" #: editor/project_manager.cpp msgid "Also delete project contents (no undo!)" -msgstr "" +msgstr "Также удалить Ñодержимое проекта (Ð½ÐµÐ»ÑŒÐ·Ñ Ð¾Ñ‚Ð¼ÐµÐ½Ð¸Ñ‚ÑŒ!)" #: editor/project_manager.cpp msgid "Can't run project" @@ -11122,18 +11020,16 @@ msgstr "" "Хотите изучить официальные примеры в Библиотеке реÑурÑов?" #: editor/project_manager.cpp -#, fuzzy msgid "Filter projects" -msgstr "Фильтр ÑвойÑтв" +msgstr "Фильтр проектов" #: editor/project_manager.cpp -#, fuzzy msgid "" "This field filters projects by name and last path component.\n" "To filter projects by name and full path, the query must contain at least " "one `/` character." msgstr "" -"Поле поиÑка фильтрует проекты по имени и поÑледнему компоненту пути\n" +"Ðто поле фильтрует проекты по имени и поÑледнему компоненту пути.\n" "Чтобы отфильтровать проекты по имени и полному пути, Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð´Ð¾Ð»Ð¶ÐµÐ½ Ñодержать " "Ñ…Ð¾Ñ‚Ñ Ð±Ñ‹ один Ñимвол `/`." @@ -11143,7 +11039,7 @@ msgstr "Клавиша " #: editor/project_settings_editor.cpp msgid "Physical Key" -msgstr "" +msgstr "ФизичеÑÐºÐ°Ñ ÐºÐ»Ð°Ð²Ð¸ÑˆÐ°" #: editor/project_settings_editor.cpp msgid "Joy Button" @@ -11191,7 +11087,7 @@ msgstr "УÑтройÑтво" #: editor/project_settings_editor.cpp msgid " (Physical)" -msgstr "" +msgstr " (ФизичеÑкаÑ)" #: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp msgid "Press a Key..." @@ -11334,23 +11230,20 @@ msgid "Override for Feature" msgstr "Переопределение ÑвойÑтва" #: editor/project_settings_editor.cpp -#, fuzzy msgid "Add %d Translations" -msgstr "Добавить перевод" +msgstr "Добавить %d переводов" #: editor/project_settings_editor.cpp msgid "Remove Translation" msgstr "Удалить перевод" #: editor/project_settings_editor.cpp -#, fuzzy msgid "Translation Resource Remap: Add %d Path(s)" -msgstr "Перенаправлен реÑÑƒÑ€Ñ Ð¿ÐµÑ€ÐµÐ½Ð°Ð¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ" +msgstr "Переназначение реÑурÑов перевода: Добавить %d путь(ей)" #: editor/project_settings_editor.cpp -#, fuzzy msgid "Translation Resource Remap: Add %d Remap(s)" -msgstr "Перенаправлен реÑÑƒÑ€Ñ Ð¿ÐµÑ€ÐµÐ½Ð°Ð¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ" +msgstr "Переназначение реÑурÑов перевода: Добавить %d переназначение(ий)" #: editor/project_settings_editor.cpp msgid "Change Resource Remap Language" @@ -11766,7 +11659,7 @@ msgstr "Узел должен принадлежать редактируемоР#: editor/scene_tree_dock.cpp msgid "Instantiated scenes can't become root" -msgstr "ИнÑтанцированные Ñцены не могут Ñтать корневыми" +msgstr "ИнÑтанцированные Ñцены не могут Ñтать корнем" #: editor/scene_tree_dock.cpp msgid "Make node as Root" @@ -11796,12 +11689,16 @@ msgstr "Удалить узел «%s»?" msgid "" "Saving the branch as a scene requires having a scene open in the editor." msgstr "" +"Ð”Ð»Ñ ÑÐ¾Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð²ÐµÑ‚ÐºÐ¸ как Ñцены необходимо, чтобы Ñцена была открыта в " +"редакторе." #: editor/scene_tree_dock.cpp msgid "" "Saving the branch as a scene requires selecting only one node, but you have " "selected %d nodes." msgstr "" +"Ð”Ð»Ñ ÑÐ¾Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð²ÐµÑ‚Ð²Ð¸ как Ñцены требуетÑÑ Ð²Ñ‹Ð±Ñ€Ð°Ñ‚ÑŒ только один узел, но вы " +"выбрали %d узлов." #: editor/scene_tree_dock.cpp msgid "" @@ -11810,6 +11707,12 @@ msgid "" "FileSystem dock context menu\n" "or create an inherited scene using Scene > New Inherited Scene... instead." msgstr "" +"Ðевозможно Ñохранить ветку корневого узла в качеÑтве инÑтанцированной " +"Ñцены.\n" +"Чтобы Ñоздать редактируемую копию текущей Ñцены, продублируйте её Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ " +"контекÑтного меню панели Â«Ð¤Ð°Ð¹Ð»Ð¾Ð²Ð°Ñ ÑиÑтема»\n" +"или Ñоздайте унаÑледованную Ñцену, иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÑ Ð²Ð¼ÐµÑто Ñтого Сцена > ÐÐ¾Ð²Ð°Ñ " +"унаÑÐ»ÐµÐ´Ð¾Ð²Ð°Ð½Ð½Ð°Ñ Ñцена..." #: editor/scene_tree_dock.cpp msgid "" @@ -11817,6 +11720,10 @@ msgid "" "To create a variation of a scene, you can make an inherited scene based on " "the instanced scene using Scene > New Inherited Scene... instead." msgstr "" +"Ðевозможно Ñохранить ветку уже инÑтанцированной Ñцены.\n" +"Чтобы Ñоздать вариант Ñцены, вы можете Ñоздать унаÑледованную Ñцену на " +"оÑнове инÑтанцированной Ñцены, иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÑ Ð²Ð¼ÐµÑто Ñтого Сцена > ÐÐ¾Ð²Ð°Ñ " +"унаÑÐ»ÐµÐ´Ð¾Ð²Ð°Ð½Ð½Ð°Ñ Ñцена..." #: editor/scene_tree_dock.cpp msgid "Save New Scene As..." @@ -12225,6 +12132,8 @@ msgid "" "Warning: Having the script name be the same as a built-in type is usually " "not desired." msgstr "" +"Предупреждение: Обычно нежелательно, чтобы Ð¸Ð¼Ñ Ñкрипта Ñовпадало Ñ Ð¸Ð¼ÐµÐ½ÐµÐ¼ " +"вÑтроенного типа." #: editor/script_create_dialog.cpp msgid "Class Name:" @@ -12296,7 +12205,7 @@ msgstr "Копировать ошибку" #: editor/script_editor_debugger.cpp msgid "Open C++ Source on GitHub" -msgstr "" +msgstr "Открытый иÑходный код C++ на GitHub" #: editor/script_editor_debugger.cpp msgid "Video RAM" @@ -12475,6 +12384,16 @@ msgstr "Изменить выÑоту цилиндра" msgid "Change Ray Shape Length" msgstr "Изменить длину луча" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "УÑтановить положение точки кривой" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "УÑтановить положение точки кривой" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "Изменить Ñ€Ð°Ð´Ð¸ÑƒÑ Ñ†Ð¸Ð»Ð¸Ð½Ð´Ñ€Ð°" @@ -12585,14 +12504,12 @@ msgid "Object can't provide a length." msgstr "Объект не может предоÑтавить длину." #: modules/gltf/editor_scene_exporter_gltf_plugin.cpp -#, fuzzy msgid "Export Mesh GLTF2" -msgstr "ÐкÑпортировать библиотеку полиÑеток" +msgstr "ÐкÑпорт полиÑетки GLTF2" #: modules/gltf/editor_scene_exporter_gltf_plugin.cpp -#, fuzzy msgid "Export GLTF..." -msgstr "ÐкÑпортировать..." +msgstr "ÐкÑпорт GLTF..." #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Next Plane" @@ -12624,20 +12541,19 @@ msgstr "Удалить выделенную Ñетку" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "GridMap Fill Selection" -msgstr "Залить выделенную GridMap" +msgstr "Залить выделенную Ñетку" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "GridMap Paste Selection" -msgstr "Ð’Ñтавка выделенной Ñетки" +msgstr "Ð’Ñтавить выделенную Ñетку" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "GridMap Paint" msgstr "РиÑование Ñетки" #: modules/gridmap/grid_map_editor_plugin.cpp -#, fuzzy msgid "GridMap Selection" -msgstr "Залить выделенную GridMap" +msgstr "Выделение Ñетки" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Grid Map" @@ -12653,11 +12569,11 @@ msgstr "Отключить обрезку" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Clip Above" -msgstr "Отрезать Ñверху" +msgstr "Обрезать Ñверху" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Clip Below" -msgstr "Отрезать Ñнизу" +msgstr "Обрезать Ñнизу" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Edit X Axis" @@ -12889,14 +12805,12 @@ msgid "Add Output Port" msgstr "Добавить выходной порт" #: modules/visual_script/visual_script_editor.cpp -#, fuzzy msgid "Change Port Type" -msgstr "Изменить тип" +msgstr "Изменить тип порта" #: modules/visual_script/visual_script_editor.cpp -#, fuzzy msgid "Change Port Name" -msgstr "Изменить Ð¸Ð¼Ñ Ð²Ñ…Ð¾Ð´Ð½Ð¾Ð³Ð¾ порта" +msgstr "Изменить Ð¸Ð¼Ñ Ð¿Ð¾Ñ€Ñ‚Ð°" #: modules/visual_script/visual_script_editor.cpp msgid "Override an existing built-in function." @@ -13011,9 +12925,8 @@ msgid "Add Preload Node" msgstr "Добавить предзагрузочный узел" #: modules/visual_script/visual_script_editor.cpp -#, fuzzy msgid "Add Node(s)" -msgstr "Добавить узел" +msgstr "Добавить узел(узлы)" #: modules/visual_script/visual_script_editor.cpp msgid "Add Node(s) From Tree" @@ -13276,37 +13189,31 @@ msgstr "Выберите уÑтройÑтво из ÑпиÑка" #: platform/android/export/export.cpp msgid "Running on %s" -msgstr "" +msgstr "ВыполнÑетÑÑ Ð½Ð° %s" #: platform/android/export/export.cpp -#, fuzzy msgid "Exporting APK..." -msgstr "ÐкÑпорт вÑех" +msgstr "ÐкÑпорт APK..." #: platform/android/export/export.cpp -#, fuzzy msgid "Uninstalling..." -msgstr "Удалить" +msgstr "Удаление..." #: platform/android/export/export.cpp -#, fuzzy msgid "Installing to device, please wait..." -msgstr "Загрузка, пожалуйÑта, ждите..." +msgstr "УÑтановка на уÑтройÑтво, пожалуйÑта, ждите..." #: platform/android/export/export.cpp -#, fuzzy msgid "Could not install to device: %s" -msgstr "Ðе возможно добавить Ñцену!" +msgstr "Ðе удалоÑÑŒ уÑтановить на уÑтройÑтво: %s" #: platform/android/export/export.cpp -#, fuzzy msgid "Running on device..." -msgstr "ЗапуÑк пользовательÑкого Ñкрипта..." +msgstr "ЗапуÑк на уÑтройÑтве..." #: platform/android/export/export.cpp -#, fuzzy msgid "Could not execute on device." -msgstr "Ðевозможно Ñоздать папку." +msgstr "Ðе удалоÑÑŒ выполнить на уÑтройÑтве." #: platform/android/export/export.cpp msgid "Unable to find the 'apksigner' tool." @@ -13431,40 +13338,37 @@ msgid "" "directory.\n" "The resulting %s is unsigned." msgstr "" +"Ðе удалоÑÑŒ найти команду «apksigner».\n" +"ПожалуйÑта, проверьте наличие программы в каталоге Android SDK build-tools.\n" +"Результат %s не подпиÑан." #: platform/android/export/export.cpp msgid "Signing debug %s..." -msgstr "" +msgstr "ПодпиÑание отладочного %s..." #: platform/android/export/export.cpp -#, fuzzy msgid "Signing release %s..." -msgstr "" -"Сканирование файлов,\n" -"пожалуйÑта, ждите..." +msgstr "ПодпиÑание релиза %s..." #: platform/android/export/export.cpp -#, fuzzy msgid "Could not find keystore, unable to export." -msgstr "Ðе удалоÑÑŒ открыть шаблон Ð´Ð»Ñ ÑкÑпорта:" +msgstr "Ðе удалоÑÑŒ найти хранилище ключей, невозможно ÑкÑпортировать." #: platform/android/export/export.cpp msgid "'apksigner' returned with error #%d" -msgstr "" +msgstr "«apksigner» завершилÑÑ Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ¾Ð¹ #%d" #: platform/android/export/export.cpp -#, fuzzy msgid "Verifying %s..." -msgstr "Добавление %s..." +msgstr "Проверка %s..." #: platform/android/export/export.cpp msgid "'apksigner' verification of %s failed." -msgstr "" +msgstr "Проверка «apksigner» «%s» не удалаÑÑŒ." #: platform/android/export/export.cpp -#, fuzzy msgid "Exporting for Android" -msgstr "ÐкÑпорт вÑех" +msgstr "ÐкÑпорт Ð´Ð»Ñ Android" #: platform/android/export/export.cpp msgid "Invalid filename! Android App Bundle requires the *.aab extension." @@ -13480,7 +13384,7 @@ msgstr "Ðеверное Ð¸Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð°! Android APK требует раÑшР#: platform/android/export/export.cpp msgid "Unsupported export format!\n" -msgstr "" +msgstr "Ðеподдерживаемый формат ÑкÑпорта!\n" #: platform/android/export/export.cpp msgid "" @@ -13506,16 +13410,15 @@ msgstr "" msgid "" "Unable to overwrite res://android/build/res/*.xml files with project name" msgstr "" +"Ðевозможно перезапиÑать файлы res://android/build/res/*.xml Ñ Ð¸Ð¼ÐµÐ½ÐµÐ¼ проекта" #: platform/android/export/export.cpp -#, fuzzy msgid "Could not export project files to gradle project\n" -msgstr "ОтÑутÑтвует project.godot в папке проекта." +msgstr "Ðе удалоÑÑŒ ÑкÑпортировать файлы проекта в проект gradle\n" #: platform/android/export/export.cpp -#, fuzzy msgid "Could not write expansion package file!" -msgstr "Ðе удалоÑÑŒ запиÑать файл:" +msgstr "Ðе удалоÑÑŒ запиÑать раÑширение файла пакета!" #: platform/android/export/export.cpp msgid "Building Android Project (gradle)" @@ -13543,21 +13446,20 @@ msgstr "" "проекта gradle на наличие выходных данных." #: platform/android/export/export.cpp -#, fuzzy msgid "Package not found: %s" -msgstr "ÐÐ½Ð¸Ð¼Ð°Ñ†Ð¸Ñ Ð½Ðµ найдена: %s" +msgstr "Пакет не найден: %s" #: platform/android/export/export.cpp -#, fuzzy msgid "Creating APK..." -msgstr "Создание контуров..." +msgstr "Создание APK..." #: platform/android/export/export.cpp -#, fuzzy msgid "" "Could not find template APK to export:\n" "%s" -msgstr "Ðе удалоÑÑŒ открыть шаблон Ð´Ð»Ñ ÑкÑпорта:" +msgstr "" +"Ðе удалоÑÑŒ найти шаблон APK Ð´Ð»Ñ ÑкÑпорта:\n" +"%s" #: platform/android/export/export.cpp msgid "" @@ -13566,16 +13468,17 @@ msgid "" "Please build a template with all required libraries, or uncheck the missing " "architectures in the export preset." msgstr "" +"Ð’ шаблоне ÑкÑпорта отÑутÑтвуют библиотеки Ð´Ð»Ñ Ð²Ñ‹Ð±Ñ€Ð°Ð½Ð½Ñ‹Ñ… архитектур: %s.\n" +"ПожалуйÑта, Ñоздайте шаблон Ñо вÑеми необходимыми библиотеками или Ñнимите " +"флажки Ñ Ð¾Ñ‚ÑутÑтвующих архитектур в преÑете ÑкÑпорта." #: platform/android/export/export.cpp -#, fuzzy msgid "Adding files..." -msgstr "Добавление %s..." +msgstr "Добавление файлов..." #: platform/android/export/export.cpp -#, fuzzy msgid "Could not export project files" -msgstr "Ðе удалоÑÑŒ запиÑать файл:" +msgstr "Ðе удалоÑÑŒ ÑкÑпортировать файлы проекта" #: platform/android/export/export.cpp msgid "Aligning APK..." @@ -13583,7 +13486,7 @@ msgstr "Выравнивание APK..." #: platform/android/export/export.cpp msgid "Could not unzip temporary unaligned APK." -msgstr "" +msgstr "Ðе удалоÑÑŒ раÑпаковать временный невыровненный APK." #: platform/iphone/export/export.cpp platform/osx/export/export.cpp msgid "Identifier is missing." @@ -13630,45 +13533,40 @@ msgid "Could not write file:" msgstr "Ðе удалоÑÑŒ запиÑать файл:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Could not read file:" -msgstr "Ðе удалоÑÑŒ запиÑать файл:" +msgstr "Ðе удалоÑÑŒ прочитать файл:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Could not read HTML shell:" -msgstr "Ðе удалоÑÑŒ прочитать пользовательÑкую HTML оболочку:" +msgstr "Ðе удалоÑÑŒ прочитать HTML-оболочку:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Could not create HTTP server directory:" -msgstr "Ðевозможно Ñоздать папку." +msgstr "Ðе удалоÑÑŒ Ñоздать каталог HTTP-Ñервера:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Error starting HTTP server:" -msgstr "Ошибка ÑÐ¾Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ñцены." +msgstr "Ошибка запуÑка HTTP-Ñервера:" #: platform/osx/export/export.cpp -#, fuzzy msgid "Invalid bundle identifier:" -msgstr "Ðеверный идентификатор:" +msgstr "Ðеверный идентификатор пакета:" #: platform/osx/export/export.cpp msgid "Notarization: code signing required." -msgstr "" +msgstr "Предупреждение: требуетÑÑ Ð¿Ð¾Ð´Ð¿Ð¸Ñание кода." #: platform/osx/export/export.cpp msgid "Notarization: hardened runtime required." -msgstr "" +msgstr "Предупреждение: требуетÑÑ ÑƒÑиленный рантайм." #: platform/osx/export/export.cpp msgid "Notarization: Apple ID name not specified." -msgstr "" +msgstr "Предупреждение: Ð¸Ð¼Ñ Apple ID не указано." #: platform/osx/export/export.cpp msgid "Notarization: Apple ID password not specified." -msgstr "" +msgstr "Предупреждение: пароль Apple ID не указан." #: platform/uwp/export/export.cpp msgid "Invalid package short name." @@ -14102,6 +14000,9 @@ msgid "" "longer has any effect.\n" "To remove this warning, disable the GIProbe's Compress property." msgstr "" +"СвойÑтво GIProbe Compress было объÑвлено уÑтаревшим из-за извеÑтных ошибок и " +"больше не имеет никакого Ñффекта.\n" +"Чтобы убрать Ñто предупреждение, отключите ÑвойÑтво Compress в GIProbe." #: scene/3d/light.cpp msgid "A SpotLight with an angle wider than 90 degrees cannot cast shadows." @@ -14187,15 +14088,15 @@ msgstr "Узел Ри Узел Ð’ должны быть различными о #: scene/3d/portal.cpp msgid "The RoomManager should not be a child or grandchild of a Portal." -msgstr "" +msgstr "RoomManager не должен быть ребёнком или внуком Portal." #: scene/3d/portal.cpp msgid "A Room should not be a child or grandchild of a Portal." -msgstr "" +msgstr "Room не должен быть ребёнком или внуком Portal." #: scene/3d/portal.cpp msgid "A RoomGroup should not be a child or grandchild of a Portal." -msgstr "" +msgstr "RoomGroup не должен быть ребёнком или внуком Portal." #: scene/3d/remote_transform.cpp msgid "" @@ -14207,42 +14108,83 @@ msgstr "" #: scene/3d/room.cpp msgid "A Room cannot have another Room as a child or grandchild." -msgstr "" +msgstr "Room не должен быть ребёнком или внуком другого Room." #: scene/3d/room.cpp msgid "The RoomManager should not be placed inside a Room." -msgstr "" +msgstr "RoomManager не должен раÑполагатьÑÑ Ð²Ð½ÑƒÑ‚Ñ€Ð¸ Room." #: scene/3d/room.cpp msgid "A RoomGroup should not be placed inside a Room." -msgstr "" +msgstr "RoomGroup не должен раÑполагатьÑÑ Ð²Ð½ÑƒÑ‚Ñ€Ð¸ Room." #: scene/3d/room.cpp msgid "" "Room convex hull contains a large number of planes.\n" "Consider simplifying the room bound in order to increase performance." msgstr "" +"Выпуклый ÐºÐ¾Ñ€Ð¿ÑƒÑ ÐºÐ¾Ð¼Ð½Ð°Ñ‚Ñ‹ Ñодержит большое количеÑтво плоÑкоÑтей.\n" +"РаÑÑмотрите возможноÑть ÑƒÐ¿Ñ€Ð¾Ñ‰ÐµÐ½Ð¸Ñ Ð³Ñ€Ð°Ð½Ð¸Ñ†Ñ‹ комнаты Ð´Ð»Ñ Ð¿Ð¾Ð²Ñ‹ÑˆÐµÐ½Ð¸Ñ " +"производительноÑти." #: scene/3d/room_group.cpp msgid "The RoomManager should not be placed inside a RoomGroup." -msgstr "" +msgstr "RoomManager не должен раÑполагатьÑÑ Ð²Ð½ÑƒÑ‚Ñ€Ð¸ RoomGroup." #: scene/3d/room_manager.cpp msgid "The RoomList has not been assigned." -msgstr "" +msgstr "RoomList не был назначен." #: scene/3d/room_manager.cpp msgid "The RoomList node should be a Spatial (or derived from Spatial)." -msgstr "" +msgstr "Узел RoomList должен быть Spatial (или унаÑледован от Spatial)." #: scene/3d/room_manager.cpp msgid "" "Portal Depth Limit is set to Zero.\n" "Only the Room that the Camera is in will render." msgstr "" +"Portal Depth Limit уÑтановлено на ноль.\n" +"Будет отриÑовыватьÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ комната, в которой находитÑÑ ÐºÐ°Ð¼ÐµÑ€Ð°." #: scene/3d/room_manager.cpp msgid "There should only be one RoomManager in the SceneTree." +msgstr "Ð’ SceneTree должен быть только один RoomManager." + +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." msgstr "" #: scene/3d/soft_body.cpp @@ -14309,7 +14251,7 @@ msgstr "ÐÐ½Ð¸Ð¼Ð°Ñ†Ð¸Ñ Ð½Ðµ найдена: %s" #: scene/animation/animation_player.cpp msgid "Anim Apply Reset" -msgstr "" +msgstr "ÐÐ½Ð¸Ð¼Ð°Ñ†Ð¸Ñ - Применить ÑброÑ" #: scene/animation/animation_tree.cpp msgid "In node '%s', invalid animation: '%s'." @@ -14484,25 +14426,28 @@ msgid "Invalid comparison function for that type." msgstr "ÐÐµÐ²ÐµÑ€Ð½Ð°Ñ Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ ÑÑ€Ð°Ð²Ð½ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ñтого типа." #: servers/visual/shader_language.cpp -#, fuzzy msgid "Varying may not be assigned in the '%s' function." -msgstr "Ð˜Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð¼Ð¾Ð³ÑƒÑ‚ быть назначены только в функции вершины." +msgstr "Varying не может быть задано в функции «%s»." #: servers/visual/shader_language.cpp msgid "" "Varyings which assigned in 'vertex' function may not be reassigned in " "'fragment' or 'light'." msgstr "" +"Varying, заданные в функции «vertex», не могут быть изменены в «fragment» " +"или «light»." #: servers/visual/shader_language.cpp msgid "" "Varyings which assigned in 'fragment' function may not be reassigned in " "'vertex' or 'light'." msgstr "" +"Varying, заданные в функции «fragment», не могут быть изменены в «vertex» " +"или «light»." #: servers/visual/shader_language.cpp msgid "Fragment-stage varying could not been accessed in custom function!" -msgstr "" +msgstr "Varying Ñтадии fragment не доÑтупны из пользовательÑких функций!" #: servers/visual/shader_language.cpp msgid "Assignment to function." @@ -15507,9 +15452,6 @@ msgstr "КонÑтанты не могут быть изменены." #~ msgid "I see..." #~ msgstr "ЯÑно..." -#~ msgid "Can't open '%s'." -#~ msgstr "Ðе удаётÑÑ Ð¾Ñ‚ÐºÑ€Ñ‹Ñ‚ÑŒ '%s'." - #~ msgid "Ugh" #~ msgstr "ЯÑно" diff --git a/editor/translations/si.po b/editor/translations/si.po index 94841d0879..595e0041a9 100644 --- a/editor/translations/si.po +++ b/editor/translations/si.po @@ -348,6 +348,7 @@ msgstr "සජීවීකරණ පුනරà·à·€à¶»à·Šà¶®à¶±à¶º" msgid "Remove Anim Track" msgstr "Anim ලුහුබදින්න෠ඉවà¶à·Š කරන්න" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "%s සදහ෠නව ලුහුබදින්නෙà¶à·Š à·ƒà·à¶¯à· යà¶à·”රක් ඇà¶à·”à¶½à¶à·Š කරන්න?" @@ -372,10 +373,27 @@ msgstr "à·ƒà·à¶¯à¶±à·Šà¶±" msgid "Anim Insert" msgstr "Anim ඇà¶à·”à¶½à¶à·Š කරන්න" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "à·à·Šâ€à¶»à·’à¶:" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "සජීවීකරණ à¶°à·à·€à¶šà¶º à¶à¶¸à·à¶§à¶¸ සජීවීකරණය à¶šà¶½ නොහà·à¶š, අනෙක් à¶°à·à·€à¶š පමණි." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "ලක්ෂණය ලුහුබදින්න" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Anim à·ƒà·à¶¯à¶±à·Šà¶± සහ ඇà¶à·”à¶½à¶à·Š කරන්න" @@ -942,7 +960,7 @@ msgstr "" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2237,6 +2255,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -3017,10 +3046,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp #, fuzzy msgid "Update Continuously" msgstr "අඛණ්ඩව" @@ -3627,6 +3652,14 @@ msgid "Download from:" msgstr "" #: editor/export_template_manager.cpp +msgid "Open in Web Browser" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8341,6 +8374,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Remove All Color Items" msgstr "" @@ -8367,6 +8406,12 @@ msgid "Remove All StyleBox Items" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Add Color Item" msgstr "" @@ -11809,6 +11854,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13418,6 +13471,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/sk.po b/editor/translations/sk.po index 6deda77711..54736cff85 100644 --- a/editor/translations/sk.po +++ b/editor/translations/sk.po @@ -346,6 +346,7 @@ msgstr "ZmeniÅ¥ Loop Mode Animacie" msgid "Remove Anim Track" msgstr "VymazaÅ¥ Track Animácie" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "VytvoriÅ¥ NOVà track za %s a vložiÅ¥ kľúÄ?" @@ -370,10 +371,27 @@ msgstr "VytvoriÅ¥" msgid "Anim Insert" msgstr "Animácia VložiÅ¥" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animácie" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "AnimationPlayer nemôže animovaÅ¥ sám seba, iba ostatný hráÄi." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "VlastnosÅ¥" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Animácia VytvoriÅ¥ & VložiÅ¥" @@ -945,7 +963,7 @@ msgstr "VytvoriÅ¥ Nový %s" msgid "No results for \"%s\"." msgstr "Žiadne výsledky pre \"%s\"." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2295,6 +2313,17 @@ msgid "New Window" msgstr "Nové Okno" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "OtáÄa sa, keÄ sa okno editora redistribuuje." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Importované zdroje nemôžu byÅ¥ uložené." @@ -3150,10 +3179,6 @@ msgid "Save & Restart" msgstr "UložiÅ¥ & ReÅ¡tartovaÅ¥" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "OtáÄa sa, keÄ sa okno editora redistribuuje." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "AktualizovaÅ¥ priebežne" @@ -3812,6 +3837,15 @@ msgid "Download from:" msgstr "Chyba SÅ¥ahovania" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "OtvoriÅ¥ v File Manažérovy" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8677,6 +8711,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "VÅ¡etky vybrané" @@ -8707,6 +8747,12 @@ msgid "Remove All StyleBox Items" msgstr "VÅ¡etky vybrané" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "PridaÅ¥ do Obľúbených" @@ -12299,6 +12345,16 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "VÅ¡etky vybrané" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "VÅ¡etky vybrané" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13978,6 +14034,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/sl.po b/editor/translations/sl.po index 53b4bca499..725f88f0ab 100644 --- a/editor/translations/sl.po +++ b/editor/translations/sl.po @@ -369,6 +369,7 @@ msgstr "Spremeni Zanko Animacije" msgid "Remove Anim Track" msgstr "Odstrani animacijsko sled" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Ustvarim NOVO sled za %s in vstavi kljuÄ?" @@ -393,10 +394,28 @@ msgstr "Ustvari" msgid "Anim Insert" msgstr "Vstavi Animacijo" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "NaÄin Zaskoka (%s)" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animacija" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "Lastnosti" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Ustvari & Vstavi Animacijo" @@ -998,7 +1017,7 @@ msgstr "Ustvari Nov %s" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2397,6 +2416,18 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +#, fuzzy +msgid "Spins when the editor window redraws." +msgstr "Vrti se ob spremembi okna urejevalnika!" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -3270,11 +3301,6 @@ msgstr "Shrani & Zapri" #: editor/editor_node.cpp #, fuzzy -msgid "Spins when the editor window redraws." -msgstr "Vrti se ob spremembi okna urejevalnika!" - -#: editor/editor_node.cpp -#, fuzzy msgid "Update Continuously" msgstr "Neprekinjeno" @@ -3922,6 +3948,15 @@ msgid "Download from:" msgstr "Napaka Pri Prenosu" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Pokaži V Upravitelju Datotek" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8959,6 +8994,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Odstrani Vse Stvari" @@ -8989,6 +9030,12 @@ msgid "Remove All StyleBox Items" msgstr "Odstrani Vse Stvari" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Priljubljene:" @@ -12643,6 +12690,16 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "Nastavi Položaj Krivuljne ToÄke" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "Nastavi Položaj Krivuljne ToÄke" + #: modules/csg/csg_gizmos.cpp #, fuzzy msgid "Change Cylinder Radius" @@ -14339,6 +14396,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" @@ -14856,9 +14949,6 @@ msgstr "Konstante ni možno spreminjati." #~ msgid "Scale Mode (R)" #~ msgstr "NaÄin Obsega (R)" -#~ msgid "Snap Mode (%s)" -#~ msgstr "NaÄin Zaskoka (%s)" - #~ msgid "Tool Select" #~ msgstr "Izbira Orodja" diff --git a/editor/translations/sq.po b/editor/translations/sq.po index f843d97c4e..ded08d5532 100644 --- a/editor/translations/sq.po +++ b/editor/translations/sq.po @@ -342,6 +342,7 @@ msgstr "Ndrysho Metodën e Përsëritjes së Animacionit" msgid "Remove Anim Track" msgstr "" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "" @@ -366,10 +367,27 @@ msgstr "Krijo" msgid "Anim Insert" msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animacionet:" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "Vetitë:" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "" @@ -938,7 +956,7 @@ msgstr "Krijo %s të ri" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2330,6 +2348,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "Rrotullohet kur dritarja e editorit rivizaton." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Resurset e importuara nuk mund të ruhen." @@ -3207,10 +3236,6 @@ msgid "Save & Restart" msgstr "Ruaj & Rifillo" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "Rrotullohet kur dritarja e editorit rivizaton." - -#: editor/editor_node.cpp #, fuzzy msgid "Update Continuously" msgstr "I Vazhdueshëm" @@ -3863,6 +3888,15 @@ msgid "Download from:" msgstr "Shkarko" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Hap në Menaxherin e Skedarëve" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8684,6 +8718,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Hiq Autoload-in" @@ -8714,6 +8754,12 @@ msgid "Remove All StyleBox Items" msgstr "Hiq Autoload-in" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Shto te të preferuarat" @@ -12247,6 +12293,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13891,6 +13945,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/sr_Cyrl.po b/editor/translations/sr_Cyrl.po index f95781bd70..0a915e03bf 100644 --- a/editor/translations/sr_Cyrl.po +++ b/editor/translations/sr_Cyrl.po @@ -382,6 +382,7 @@ msgstr "Промени Ñ†Ð¸ÐºÐ»ÑƒÑ Ð°Ð½Ð¸Ð¼Ð°Ñ†Ð¸Ñ˜Ðµ" msgid "Remove Anim Track" msgstr "Обриши траку анимације" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Ðаправите нову траку за %s и убаците кључ?" @@ -406,11 +407,29 @@ msgstr "Ðаправи" msgid "Anim Insert" msgstr "Ðалепи" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "Режим лепљења:" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Ðнимација" + #: editor/animation_track_editor.cpp #, fuzzy msgid "AnimationPlayer can't animate itself, only other players." msgstr "AnimationPlayer не може Ñам Ñебе да анимира, Ñамо друге плејере." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "ОÑобина %s' не поÑтоји." + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Ðаправи анимацију и убаци" @@ -1051,7 +1070,7 @@ msgstr "Ðаправи нов" msgid "No results for \"%s\"." msgstr "Ðема резултата за \"%s\"." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2502,6 +2521,18 @@ msgid "New Window" msgstr "Ðов Прозор" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +#, fuzzy +msgid "Spins when the editor window redraws." +msgstr "Окрене Ñе кад Ñе едиторÑки прозор поново обоји!" + +#: editor/editor_node.cpp #, fuzzy msgid "Imported resources can't be saved." msgstr "Увезени реÑурÑи не могу бити упамћени." @@ -3397,11 +3428,6 @@ msgstr "Сачувај и изађи" #: editor/editor_node.cpp #, fuzzy -msgid "Spins when the editor window redraws." -msgstr "Окрене Ñе кад Ñе едиторÑки прозор поново обоји!" - -#: editor/editor_node.cpp -#, fuzzy msgid "Update Continuously" msgstr "Трајан" @@ -4106,6 +4132,16 @@ msgid "Download from:" msgstr "Грешка при преузимању" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Покрени у Претраживачу" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "Копирај Грешку" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -9423,6 +9459,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Обриши Ñве Ñтавке" @@ -9453,6 +9495,12 @@ msgid "Remove All StyleBox Items" msgstr "Обриши Ñве Ñтавке" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Додај Ñтавке клаÑе" @@ -13751,6 +13799,16 @@ msgstr "Промени ВиÑину Цилиндар Облика" msgid "Change Ray Shape Length" msgstr "Промени Дужину Зрак Облика" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "ПоÑтави позицију тачке криве" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "ПоÑтави позицију тачке криве" + #: modules/csg/csg_gizmos.cpp #, fuzzy msgid "Change Cylinder Radius" @@ -15695,6 +15753,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp #, fuzzy msgid "This body will be ignored until you set a mesh." @@ -16456,10 +16550,6 @@ msgstr "КонÑтанте није могуће мењати." #~ msgid "Local Coords" #~ msgstr "Локалне координате" -#, fuzzy -#~ msgid "Snap Mode (%s)" -#~ msgstr "Режим лепљења:" - #~ msgid "Tool Select" #~ msgstr "Избор алатки" diff --git a/editor/translations/sr_Latn.po b/editor/translations/sr_Latn.po index 877149b6ea..76982c0b00 100644 --- a/editor/translations/sr_Latn.po +++ b/editor/translations/sr_Latn.po @@ -356,6 +356,7 @@ msgstr "Optimizuj Animaciju" msgid "Remove Anim Track" msgstr "Odstrani Kanal Animacije" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Napravi Novi kanal za %s i dodaj kljuÄ?" @@ -380,10 +381,26 @@ msgstr "Napravi" msgid "Anim Insert" msgstr "Animacija Umetni" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Optimizuj Animaciju" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "property '%s'" +msgstr "" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Animacija Napravi i Dodaj" @@ -950,7 +967,7 @@ msgstr "" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2250,6 +2267,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -3033,10 +3061,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp #, fuzzy msgid "Update Continuously" msgstr "Neprekidna" @@ -3646,6 +3670,14 @@ msgid "Download from:" msgstr "" #: editor/export_template_manager.cpp +msgid "Open in Web Browser" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8405,6 +8437,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Remove All Color Items" msgstr "" @@ -8432,6 +8470,12 @@ msgid "Remove All StyleBox Items" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Add Color Item" msgstr "" @@ -11915,6 +11959,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13530,6 +13582,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/sv.po b/editor/translations/sv.po index 7433664d25..373e3aad36 100644 --- a/editor/translations/sv.po +++ b/editor/translations/sv.po @@ -14,7 +14,7 @@ # Mattias Münster <mattiasmun@gmail.com>, 2019. # Anonymous <noreply@weblate.org>, 2020. # Joakim Lundberg <joakim@joakimlundberg.com>, 2020. -# Kristoffer Grundström <swedishsailfishosuser@tutanota.com>, 2020. +# Kristoffer Grundström <swedishsailfishosuser@tutanota.com>, 2020, 2021. # Jonas Robertsson <jonas.robertsson@posteo.net>, 2020, 2021. # André Andersson <andre.eric.andersson@gmail.com>, 2020. # Andreas Westrell <andreas.westrell@gmail.com>, 2020. @@ -27,8 +27,8 @@ msgid "" msgstr "" "Project-Id-Version: Godot Engine editor\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2021-07-13 06:13+0000\n" -"Last-Translator: Leon <joel.lundborg@gmail.com>\n" +"PO-Revision-Date: 2021-08-10 21:40+0000\n" +"Last-Translator: Kristoffer Grundström <swedishsailfishosuser@tutanota.com>\n" "Language-Team: Swedish <https://hosted.weblate.org/projects/godot-engine/" "godot/sv/>\n" "Language: sv\n" @@ -36,7 +36,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.7.2-dev\n" +"X-Generator: Weblate 4.8-dev\n" #: core/math/expression.cpp modules/gdscript/gdscript_functions.cpp #: modules/visual_script/visual_script_builtin_funcs.cpp @@ -357,6 +357,7 @@ msgstr "Ändra Animationsslingläge" msgid "Remove Anim Track" msgstr "Ta bort Anim spÃ¥r" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Skapa NYTT spÃ¥r för %s och infoga nyckel?" @@ -381,11 +382,29 @@ msgstr "Skapa" msgid "Anim Insert" msgstr "Anim Infoga" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "Kan inte öppna '%s'." + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animation" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" "Animationsspelaren kan inte animera sig själv, utan bara andra spelare." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "Egenskaper" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Anim Skapa & Infoga" @@ -473,7 +492,7 @@ msgstr "Anim Flytta Nycklar" #: editor/animation_track_editor.cpp #: modules/visual_script/visual_script_editor.cpp msgid "Clipboard is empty!" -msgstr "" +msgstr "Klippbordet är tomt!" #: editor/animation_track_editor.cpp msgid "Paste Tracks" @@ -943,9 +962,8 @@ msgid "Edit..." msgstr "Ändra..." #: editor/connections_dialog.cpp -#, fuzzy msgid "Go to Method" -msgstr "GÃ¥ Till Metod" +msgstr "GÃ¥ till metod" #: editor/create_dialog.cpp msgid "Change %s Type" @@ -963,9 +981,9 @@ msgstr "Skapa Ny %s" msgid "No results for \"%s\"." msgstr "Inga resultat för \"%s\"." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." -msgstr "" +msgstr "Ingen beskrivning tillgänglig för %s." #: editor/create_dialog.cpp editor/editor_file_dialog.cpp #: editor/filesystem_dock.cpp @@ -1075,7 +1093,6 @@ msgstr "" "Du kan hitta de borttagna filerna i systemets papperskorg." #: editor/dependency_editor.cpp -#, fuzzy msgid "" "The files being removed are required by other resources in order for them to " "work.\n" @@ -1083,9 +1100,10 @@ msgid "" "Depending on your filesystem configuration, the files will either be moved " "to the system trash or deleted permanently." msgstr "" -"Filerna som tas bort krävs av andra resurser för att de ska fungera.\n" -"Ta bort dem ändÃ¥? (gÃ¥r inte Ã¥ngra)\n" -"Du kan hitta de borttagna filerna i systemets papperskorg." +"Filerna som ska tas bort krävs av andra resurser för att de ska fungera.\n" +"Ta bort dem ändÃ¥? (GÃ¥r inte Ã¥ngra.)\n" +"Beroende pÃ¥ hur ditt filsystem är inställt sÃ¥ kommer filerna antingen " +"flyttas till systemets papperskorg eller tas bort permanent." #: editor/dependency_editor.cpp msgid "Cannot remove:" @@ -1260,9 +1278,8 @@ msgid "Error opening asset file for \"%s\" (not in ZIP format)." msgstr "Fel vid öppning av paketetfil, inte i zip-format." #: editor/editor_asset_installer.cpp -#, fuzzy msgid "%s (already exists)" -msgstr "%s (Existerar Redan)" +msgstr "%s (existerar redan)" #: editor/editor_asset_installer.cpp msgid "Contents of asset \"%s\" - %d file(s) conflict with your project:" @@ -1282,9 +1299,8 @@ msgid "The following files failed extraction from asset \"%s\":" msgstr "Följande filer misslyckades att packas upp frÃ¥n paketet:" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "(and %s more files)" -msgstr "%d fler filer." +msgstr "(och %s fler filer)" #: editor/editor_asset_installer.cpp #, fuzzy @@ -1534,9 +1550,8 @@ msgid "Can't add autoload:" msgstr "Kunde inte lägga till autoladdning:" #: editor/editor_autoload_settings.cpp -#, fuzzy msgid "%s is an invalid path. File does not exist." -msgstr "Fil existerar inte." +msgstr "%s är en ogiltig genväg. Filen existerar inte." #: editor/editor_autoload_settings.cpp msgid "%s is an invalid path. Not in resource path (res://)." @@ -1564,9 +1579,8 @@ msgid "Name" msgstr "Namn" #: editor/editor_autoload_settings.cpp -#, fuzzy msgid "Global Variable" -msgstr "Variabel" +msgstr "Global variabel" #: editor/editor_data.cpp msgid "Paste Params" @@ -1663,22 +1677,20 @@ msgstr "" "Fallback Enabled'." #: editor/editor_export.cpp -#, fuzzy msgid "" "Target platform requires 'PVRTC' texture compression for GLES2. Enable " "'Import Pvrtc' in Project Settings." msgstr "" -"MÃ¥lplattformen kräver 'ETC' texturkomprimering för GLES2. Aktivera 'Import " -"Etc' i Projektinställningarna." +"MÃ¥lplattformen kräver 'PVRTC'-texturkomprimering för GLES2. Aktivera 'Import " +"Pvrtc' i projektinställningarna." #: editor/editor_export.cpp -#, fuzzy msgid "" "Target platform requires 'ETC2' or 'PVRTC' texture compression for GLES3. " "Enable 'Import Etc 2' or 'Import Pvrtc' in Project Settings." msgstr "" -"MÃ¥lplattformen kräver 'ETC2' texturkomprimering för GLES3. Aktivera 'Import " -"Etc 2' i Projektinställningarna." +"MÃ¥lplattformen kräver 'ETC2' eller 'PVRTC'-texturkomprimering för GLES3. " +"Aktivera 'Import Etc 2' eller 'Import Pvrtc' i projektinställningarna." #: editor/editor_export.cpp msgid "" @@ -1742,11 +1754,11 @@ msgstr "Importera" #: editor/editor_feature_profile.cpp msgid "Allows to view and edit 3D scenes." -msgstr "" +msgstr "TillÃ¥ter att visa och redigera 3D-scener." #: editor/editor_feature_profile.cpp msgid "Allows to edit scripts using the integrated script editor." -msgstr "" +msgstr "TillÃ¥ter att redigera skript via den integrerade skript-redigeraren." #: editor/editor_feature_profile.cpp msgid "Provides built-in access to the Asset Library." @@ -2341,6 +2353,17 @@ msgid "New Window" msgstr "Nytt Fönster" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Importerade resurser kan inte sparas." @@ -3198,10 +3221,6 @@ msgid "Save & Restart" msgstr "Spara & Avsluta" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp #, fuzzy msgid "Update Continuously" msgstr "Kontinuerlig" @@ -3844,6 +3863,16 @@ msgid "Download from:" msgstr "Ladda ner" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Kör i Webbläsare" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "Fel" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -6348,7 +6377,7 @@ msgstr "" #: editor/plugins/curve_editor_plugin.cpp msgid "Flat 1" -msgstr "" +msgstr "Platt 1" #: editor/plugins/curve_editor_plugin.cpp editor/property_editor.cpp msgid "Ease In" @@ -8789,6 +8818,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Ta bort Alla" @@ -8819,6 +8854,12 @@ msgid "Remove All StyleBox Items" msgstr "Ta bort Alla" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Lägg till i Favoriter" @@ -12425,6 +12466,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -14103,6 +14152,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" @@ -14878,9 +14963,6 @@ msgstr "" #~ msgid "I see..." #~ msgstr "Jag förstÃ¥r..." -#~ msgid "Can't open '%s'." -#~ msgstr "Kan inte öppna '%s'." - #, fuzzy #~ msgid "Ugh" #~ msgstr "Ugh" diff --git a/editor/translations/ta.po b/editor/translations/ta.po index 5b4e249318..2ad954b971 100644 --- a/editor/translations/ta.po +++ b/editor/translations/ta.po @@ -355,6 +355,7 @@ msgstr "" msgid "Remove Anim Track" msgstr "அசைவூடà¯à®Ÿà¯ பாதையை நீகà¯à®•à¯" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "" @@ -379,10 +380,26 @@ msgstr "" msgid "Anim Insert" msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "மாறà¯à®±à®™à¯à®•ளை இதறà¯à®•௠அமை:" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "property '%s'" +msgstr "" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "" @@ -945,7 +962,7 @@ msgstr "" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2241,6 +2258,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -3022,10 +3050,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "" @@ -3633,6 +3657,14 @@ msgid "Download from:" msgstr "" #: editor/export_template_manager.cpp +msgid "Open in Web Browser" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8343,6 +8375,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Remove All Color Items" msgstr "" @@ -8370,6 +8408,12 @@ msgid "Remove All StyleBox Items" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Add Color Item" msgstr "" @@ -11815,6 +11859,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13423,6 +13475,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/te.po b/editor/translations/te.po index 6a4e076543..74998009cd 100644 --- a/editor/translations/te.po +++ b/editor/translations/te.po @@ -338,6 +338,7 @@ msgstr "" msgid "Remove Anim Track" msgstr "" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "" @@ -362,10 +363,25 @@ msgstr "" msgid "Anim Insert" msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "animation" +msgstr "" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "property '%s'" +msgstr "" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "" @@ -920,7 +936,7 @@ msgstr "" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2211,6 +2227,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -2991,10 +3018,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "" @@ -3599,6 +3622,14 @@ msgid "Download from:" msgstr "" #: editor/export_template_manager.cpp +msgid "Open in Web Browser" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8276,6 +8307,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Remove All Color Items" msgstr "" @@ -8300,6 +8337,12 @@ msgid "Remove All StyleBox Items" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Add Color Item" msgstr "" @@ -11708,6 +11751,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13302,6 +13353,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/th.po b/editor/translations/th.po index ce5e4952f1..231051313a 100644 --- a/editor/translations/th.po +++ b/editor/translations/th.po @@ -356,6 +356,7 @@ msgstr "เปลี่ยนโหมดà¸à¸²à¸£à¸§à¸™à¸‹à¹‰à¸³à¹à¸à¸™à¸´à¹€ msgid "Remove Anim Track" msgstr "ลบà¹à¸—ร็à¸à¹à¸à¸™à¸´à¹€à¸¡à¸Šà¸±à¸™" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "เพิ่มà¹à¸—ร็à¸à¹ƒà¸«à¸¡à¹ˆà¸ªà¸³à¸«à¸£à¸±à¸š %s à¹à¸¥à¸°à¹€à¸žà¸´à¹ˆà¸¡à¸„ีย์?" @@ -380,10 +381,28 @@ msgstr "สร้าง" msgid "Anim Insert" msgstr "à¹à¸—รà¸à¹à¸à¸™à¸´à¹€à¸¡à¸Šà¸±à¸™" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "เปิด '%s' ไม่ได้" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "à¹à¸à¸™à¸´à¹€à¸¡à¸Šà¸±à¸™" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "ตัวเล่นà¸à¸™à¸´à¹€à¸¡à¸Šà¸±à¹ˆà¸™à¹„ม่สามารถเล่นà¸à¸™à¸´à¹€à¸¡à¸Šà¸±à¹ˆà¸™à¸”้วยตัวมันเà¸à¸‡à¹„ด้ เล่นได้เฉพาะตัวเล่นà¸à¸·à¹ˆà¸™à¹€à¸—่านั้น" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "ไม่พบคุณสมบัติ '%s'" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "สร้างà¹à¸¥à¸°à¹à¸—รà¸à¹à¸à¸™à¸´à¹€à¸¡à¸Šà¸±à¸™" @@ -954,7 +973,7 @@ msgstr "สร้าง %s ใหม่" msgid "No results for \"%s\"." msgstr "ไม่มีผลลัพธ์สำหรับ \"%s\"" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2290,6 +2309,17 @@ msgid "New Window" msgstr "หน้าต่างใหม่" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "หมุนเมื่à¸à¸¡à¸µà¸à¸²à¸£à¸§à¸²à¸”หน้าต่างโปรà¹à¸à¸£à¸¡à¹ƒà¸«à¸¡" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "ทรัพยาà¸à¸£à¸—ี่นำเข้ามา ไม่สามารถบันทึà¸à¹„ด้" @@ -3122,10 +3152,6 @@ msgid "Save & Restart" msgstr "บันทึà¸à¹à¸¥à¸°à¹€à¸£à¸´à¹ˆà¸¡à¹ƒà¸«à¸¡à¹ˆ" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "หมุนเมื่à¸à¸¡à¸µà¸à¸²à¸£à¸§à¸²à¸”หน้าต่างโปรà¹à¸à¸£à¸¡à¹ƒà¸«à¸¡" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "à¸à¸±à¸žà¹€à¸”ทà¸à¸¢à¹ˆà¸²à¸‡à¸•่à¸à¹€à¸™à¸·à¹ˆà¸à¸‡" @@ -3776,6 +3802,16 @@ msgid "Download from:" msgstr "ดาวน์โหลดผิดพลาด" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "รันในเบราเซà¸à¸£à¹Œ" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "คัดลà¸à¸à¸œà¸´à¸”พลาด" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8592,6 +8628,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "ลบทั้งหมด" @@ -8622,6 +8664,12 @@ msgid "Remove All StyleBox Items" msgstr "ลบทั้งหมด" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "เพิ่มไà¸à¹€à¸—มคลาส" @@ -12217,6 +12265,16 @@ msgstr "ปรับความสูงทรงà¹à¸„ปซูล" msgid "Change Ray Shape Length" msgstr "ปรับความยาวรังสี" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "à¸à¸³à¸«à¸™à¸”พิà¸à¸±à¸”จุดเส้นโค้ง" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "à¸à¸³à¸«à¸™à¸”พิà¸à¸±à¸”จุดเส้นโค้ง" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "ปรับรัศมีทรงà¸à¸£à¸°à¸šà¸à¸" @@ -13909,6 +13967,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "วัตถุนี้จะถูà¸à¸¥à¸°à¹€à¸§à¹‰à¸™à¸ˆà¸™à¸à¸§à¹ˆà¸²à¸ˆà¸°à¸•ั้ง mesh" @@ -15120,9 +15214,6 @@ msgstr "ค่าคงที่ไม่สามารถà¹à¸à¹‰à¹„ขได #~ msgid "I see..." #~ msgstr "ตà¸à¸¥à¸‡..." -#~ msgid "Can't open '%s'." -#~ msgstr "เปิด '%s' ไม่ได้" - #~ msgid "Ugh" #~ msgstr "เà¸à¸à¸°" diff --git a/editor/translations/tr.po b/editor/translations/tr.po index 8a735113cc..69a7ef73a2 100644 --- a/editor/translations/tr.po +++ b/editor/translations/tr.po @@ -395,6 +395,7 @@ msgstr "Animasyon Döngü Kipini DeÄŸiÅŸtir" msgid "Remove Anim Track" msgstr "Animasyon İzini Kaldır" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "%s için YENİ iz oluÅŸtur ve anahtar gir?" @@ -419,11 +420,29 @@ msgstr "OluÅŸtur" msgid "Anim Insert" msgstr "Animasyon Ekle" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "'%s' açılamıyor." + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Animasyon" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" "Animasyon Oynatıcısı kendisini oynatamaz, sadece diÄŸer oynatıcılar yapabilir." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "'%s' özelliÄŸi mevcut deÄŸil." + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Animasyon OluÅŸtur & Ekle" @@ -999,7 +1018,7 @@ msgstr "Yeni %s OluÅŸtur" msgid "No results for \"%s\"." msgstr "\"%s\" için sonuç yok." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2350,6 +2369,17 @@ msgid "New Window" msgstr "Yeni Pencere" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "Düzenleyici penceresi yeniden boyandığında döner." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "İçe aktarılmış kaynaklar kaydedilemez." @@ -3212,10 +3242,6 @@ msgid "Save & Restart" msgstr "Kaydet ve BaÅŸtan BaÅŸlat" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "Düzenleyici penceresi yeniden boyandığında döner." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "Sürekli Güncelle" @@ -3878,6 +3904,16 @@ msgid "Download from:" msgstr "İndirme Hatası" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Tarayıcıda Çalıştır" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "Hatayı Kopyala" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8740,6 +8776,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Bütün Öğeleri Kaldır" @@ -8770,6 +8812,12 @@ msgid "Remove All StyleBox Items" msgstr "Bütün Öğeleri Kaldır" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Sınıf Öğeleri Ekle" @@ -12426,6 +12474,16 @@ msgstr "Silindir Åžekli YüksekliÄŸini DeÄŸiÅŸtir" msgid "Change Ray Shape Length" msgstr "Işın Åžeklinin UzunluÄŸunu DeÄŸiÅŸtir" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "EÄŸri Noktası Konumu Ayarla" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "EÄŸri Noktası Konumu Ayarla" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "Silindir Yarıçapını DeÄŸiÅŸtir" @@ -14194,6 +14252,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "Bir model ayarlanana kadar bu gövde yok sayılır." @@ -15449,9 +15543,6 @@ msgstr "Sabit deÄŸerler deÄŸiÅŸtirilemez." #~ msgid "I see..." #~ msgstr "Anlıyorum..." -#~ msgid "Can't open '%s'." -#~ msgstr "'%s' açılamıyor." - #~ msgid "Ugh" #~ msgstr "Öff" diff --git a/editor/translations/tt.po b/editor/translations/tt.po index 4ff5555363..e7b37069b7 100644 --- a/editor/translations/tt.po +++ b/editor/translations/tt.po @@ -338,6 +338,7 @@ msgstr "" msgid "Remove Anim Track" msgstr "" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "" @@ -362,10 +363,25 @@ msgstr "" msgid "Anim Insert" msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "animation" +msgstr "" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "property '%s'" +msgstr "" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "" @@ -920,7 +936,7 @@ msgstr "" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2211,6 +2227,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -2990,10 +3017,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "" @@ -3598,6 +3621,14 @@ msgid "Download from:" msgstr "" #: editor/export_template_manager.cpp +msgid "Open in Web Browser" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8275,6 +8306,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Remove All Color Items" msgstr "" @@ -8299,6 +8336,12 @@ msgid "Remove All StyleBox Items" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Add Color Item" msgstr "" @@ -11706,6 +11749,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13300,6 +13351,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/tzm.po b/editor/translations/tzm.po index 7d41f508ee..8c7d3f272c 100644 --- a/editor/translations/tzm.po +++ b/editor/translations/tzm.po @@ -336,6 +336,7 @@ msgstr "" msgid "Remove Anim Track" msgstr "" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "" @@ -360,10 +361,25 @@ msgstr "" msgid "Anim Insert" msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "animation" +msgstr "" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "property '%s'" +msgstr "" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "" @@ -918,7 +934,7 @@ msgstr "" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2209,6 +2225,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -2988,10 +3015,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "" @@ -3596,6 +3619,14 @@ msgid "Download from:" msgstr "" #: editor/export_template_manager.cpp +msgid "Open in Web Browser" +msgstr "" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8273,6 +8304,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Remove All Color Items" msgstr "" @@ -8297,6 +8334,12 @@ msgid "Remove All StyleBox Items" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp msgid "Add Color Item" msgstr "" @@ -11704,6 +11747,14 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +msgid "Set Room Point Position" +msgstr "" + +#: editor/spatial_editor_gizmos.cpp +msgid "Set Portal Point Position" +msgstr "" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13298,6 +13349,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/uk.po b/editor/translations/uk.po index adccdfd91f..a889e83e19 100644 --- a/editor/translations/uk.po +++ b/editor/translations/uk.po @@ -21,8 +21,8 @@ msgid "" msgstr "" "Project-Id-Version: Ukrainian (Godot Engine)\n" "POT-Creation-Date: \n" -"PO-Revision-Date: 2021-08-02 02:00+0000\n" -"Last-Translator: IllusiveMan196 <hamsterrv@gmail.com>\n" +"PO-Revision-Date: 2021-08-04 12:10+0000\n" +"Last-Translator: Yuri Chornoivan <yurchor@ukr.net>\n" "Language-Team: Ukrainian <https://hosted.weblate.org/projects/godot-engine/" "godot/uk/>\n" "Language: uk\n" @@ -356,6 +356,7 @@ msgstr "Змінити режим циклу анімації" msgid "Remove Anim Track" msgstr "Видалити доріжку" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Створити нову доріжку Ð´Ð»Ñ %s Ñ– вÑтавити ключ?" @@ -380,10 +381,28 @@ msgstr "Створити" msgid "Anim Insert" msgstr "Ð’Ñтавити анімацію" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "Ðеможливо відкрити '%s'." + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "ÐнімаціÑ" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "AnimationPlayer не може анімувати Ñебе, лише інших відтворювачів." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "ВлаÑтивоÑті «%s» не Ñ–Ñнує." + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Створити Ñ– вÑтавити анімацію" @@ -596,9 +615,8 @@ msgid "Go to Previous Step" msgstr "До попереднього кроку" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Apply Reset" -msgstr "Скинути" +msgstr "ЗаÑтоÑувати ÑкиданнÑ" #: editor/animation_track_editor.cpp msgid "Optimize Animation" @@ -617,9 +635,8 @@ msgid "Use Bezier Curves" msgstr "ВикориÑтовувати криві Безьє" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Create RESET Track(s)" -msgstr "Ð’Ñтавити доріжки" +msgstr "Створити доріжки RESET" #: editor/animation_track_editor.cpp msgid "Anim. Optimizer" @@ -944,7 +961,6 @@ msgid "Edit..." msgstr "Змінити…" #: editor/connections_dialog.cpp -#, fuzzy msgid "Go to Method" msgstr "Перейти до методу" @@ -964,9 +980,9 @@ msgstr "Створити новий %s" msgid "No results for \"%s\"." msgstr "Ðічого не знайдено Ð´Ð»Ñ Â«%s»." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." -msgstr "" +msgstr "Ðемає опиÑу Ð´Ð»Ñ %s." #: editor/create_dialog.cpp editor/editor_file_dialog.cpp #: editor/filesystem_dock.cpp @@ -1066,17 +1082,16 @@ msgid "Owners Of:" msgstr "ВлаÑники:" #: editor/dependency_editor.cpp -#, fuzzy msgid "" "Remove the selected files from the project? (Cannot be undone.)\n" "Depending on your filesystem configuration, the files will either be moved " "to the system trash or deleted permanently." msgstr "" "Вилучити позначені файли з проєкту? (без можливоÑті ÑкаÑувати)\n" -"Вилучені файли можна буде знайти Ñ– відновити у теці Ñмітника ÑиÑтеми." +"Залежно від конфігурації вашої файлової ÑиÑтеми, вилучені файли буде або " +"переÑунуто до теки Ñмітника, або вилучено оÑтаточно." #: editor/dependency_editor.cpp -#, fuzzy msgid "" "The files being removed are required by other resources in order for them to " "work.\n" @@ -1087,7 +1102,8 @@ msgstr "" "Файли, Ñкі ви вилучаєте, потрібні Ð´Ð»Ñ Ð·Ð°Ð±ÐµÐ·Ð¿ÐµÑ‡ÐµÐ½Ð½Ñ Ð¿Ñ€Ð°Ñ†ÐµÐ·Ð´Ð°Ñ‚Ð½Ð¾Ñті інших " "реÑурÑів.\n" "Вилучити Ñ—Ñ… попри це? (без ÑкаÑуваннÑ)\n" -"Вилучені файли можна знайти Ñ– відновити у теці Ñмітника ÑиÑтеми." +"Залежно від конфігурації вашої файлової ÑиÑтеми, вилучені файли буде або " +"переÑунуто до теки Ñмітника, або вилучено оÑтаточно." #: editor/dependency_editor.cpp msgid "Cannot remove:" @@ -1257,41 +1273,37 @@ msgid "Licenses" msgstr "Ліцензії" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Error opening asset file for \"%s\" (not in ZIP format)." -msgstr "Помилка під Ñ‡Ð°Ñ Ñпроби відкрити файл пакунка (дані не у форматі ZIP)." +msgstr "" +"Помилка під Ñ‡Ð°Ñ Ñпроби відкрити файл пакунка Ð´Ð»Ñ Â«%s» (не у форматі ZIP)." #: editor/editor_asset_installer.cpp -#, fuzzy msgid "%s (already exists)" msgstr "%s (вже Ñ–Ñнує)" #: editor/editor_asset_installer.cpp msgid "Contents of asset \"%s\" - %d file(s) conflict with your project:" -msgstr "" +msgstr "ВміÑÑ‚ пакунка «%s» — конфлікт %d файлів із вашим проєктом:" #: editor/editor_asset_installer.cpp msgid "Contents of asset \"%s\" - No files conflict with your project:" -msgstr "" +msgstr "ВміÑÑ‚ пакунка «%s» — немає конфліктів файлів із вашим проєктом:" #: editor/editor_asset_installer.cpp msgid "Uncompressing Assets" msgstr "Ð Ð¾Ð·Ð¿Ð°ÐºÐ¾Ð²ÑƒÐ²Ð°Ð½Ð½Ñ Ñ€ÐµÑурÑів" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "The following files failed extraction from asset \"%s\":" -msgstr "Ðе вдалоÑÑ Ð²Ð¸Ð´Ð¾Ð±ÑƒÑ‚Ð¸ такі файли з пакунка:" +msgstr "Ðе вдалоÑÑ Ð²Ð¸Ð´Ð¾Ð±ÑƒÑ‚Ð¸ з пакунка «%s» такі файли:" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "(and %s more files)" -msgstr "І ще %s файлів." +msgstr "(Ñ– ще %s файлів)" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Asset \"%s\" installed successfully!" -msgstr "Пакунок уÑпішно вÑтановлено!" +msgstr "Пакунок «%s» уÑпішно вÑтановлено!" #: editor/editor_asset_installer.cpp #: editor/plugins/asset_library_editor_plugin.cpp @@ -1303,9 +1315,8 @@ msgid "Install" msgstr "Ð’Ñтановити" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Asset Installer" -msgstr "Ð’Ñтановлювач пакета" +msgstr "Ð’Ñтановлювач пакукнків" #: editor/editor_audio_buses.cpp msgid "Speakers" @@ -1368,9 +1379,8 @@ msgid "Bypass" msgstr "Обхід" #: editor/editor_audio_buses.cpp -#, fuzzy msgid "Bus Options" -msgstr "Опції шини" +msgstr "Параметри шини" #: editor/editor_audio_buses.cpp editor/filesystem_dock.cpp #: editor/plugins/animation_player_editor_plugin.cpp editor/scene_tree_dock.cpp @@ -1536,13 +1546,12 @@ msgid "Can't add autoload:" msgstr "Ðе вдалоÑÑ Ð´Ð¾Ð´Ð°Ñ‚Ð¸ автозавантаженнÑ:" #: editor/editor_autoload_settings.cpp -#, fuzzy msgid "%s is an invalid path. File does not exist." -msgstr "Файл не Ñ–Ñнує." +msgstr "%s не Ñ” коректним шлÑхом. Файла не Ñ–Ñнує." #: editor/editor_autoload_settings.cpp msgid "%s is an invalid path. Not in resource path (res://)." -msgstr "" +msgstr "%s Ñ” некоректним шлÑхом. Його немає у шлÑху реÑурÑів (res://)." #: editor/editor_autoload_settings.cpp msgid "Add AutoLoad" @@ -1566,9 +1575,8 @@ msgid "Name" msgstr "Ðазва" #: editor/editor_autoload_settings.cpp -#, fuzzy msgid "Global Variable" -msgstr "Змінна" +msgstr "Загальна змінна" #: editor/editor_data.cpp msgid "Paste Params" @@ -1724,7 +1732,7 @@ msgstr "Редактор Ñкриптів" #: editor/editor_feature_profile.cpp msgid "Asset Library" -msgstr "Бібліотека реÑурÑів" +msgstr "Бібліотека пакунків" #: editor/editor_feature_profile.cpp msgid "Scene Tree Editing" @@ -1744,48 +1752,55 @@ msgstr "Бічна панель імпортуваннÑ" #: editor/editor_feature_profile.cpp msgid "Allows to view and edit 3D scenes." -msgstr "" +msgstr "Ðадає змогу переглÑдати Ñ– редагувати проÑторові Ñцени." #: editor/editor_feature_profile.cpp msgid "Allows to edit scripts using the integrated script editor." msgstr "" +"Ðадає змогу редагувати Ñкрипти за допомогою вбудованого редактора Ñкриптів." #: editor/editor_feature_profile.cpp msgid "Provides built-in access to the Asset Library." -msgstr "" +msgstr "Ðадає вбудований доÑтупу до бібліотеки пакунків." #: editor/editor_feature_profile.cpp msgid "Allows editing the node hierarchy in the Scene dock." -msgstr "" +msgstr "Ðадає змогу редагувати ієрархію вузлів на бічній панелі Ñцени." #: editor/editor_feature_profile.cpp msgid "" "Allows to work with signals and groups of the node selected in the Scene " "dock." msgstr "" +"Ðадає змогу працювати із Ñигналами Ñ– групами вузла, Ñкий позначено на бічній " +"панелі Ñцени." #: editor/editor_feature_profile.cpp msgid "Allows to browse the local file system via a dedicated dock." msgstr "" +"Ðадає змогу здійÑнювати навігацію локальною файловою ÑиÑтемою за допомогою " +"відповідної бічної панелі." #: editor/editor_feature_profile.cpp msgid "" "Allows to configure import settings for individual assets. Requires the " "FileSystem dock to function." msgstr "" +"Ðадає змогу налаштувати параметри Ñ–Ð¼Ð¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð»Ñ Ð¾ÐºÑ€ÐµÐ¼Ð¸Ñ… пакунків. " +"Потребує Ð´Ð»Ñ Ñ€Ð¾Ð±Ð¾Ñ‚Ð¸ бічної панелі FileSystem." #: editor/editor_feature_profile.cpp -#, fuzzy msgid "(current)" -msgstr "(Поточний)" +msgstr "(поточний)" #: editor/editor_feature_profile.cpp msgid "(none)" -msgstr "" +msgstr "(немає)" #: editor/editor_feature_profile.cpp msgid "Remove currently selected profile, '%s'? Cannot be undone." msgstr "" +"Вилучити поточний позначений профіль, «%s»? Дію не може бути ÑкаÑовано." #: editor/editor_feature_profile.cpp msgid "Profile must be a valid filename and must not contain '.'" @@ -1816,19 +1831,16 @@ msgid "Enable Contextual Editor" msgstr "Увімкнути контекÑтуальний редактор" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Class Properties:" -msgstr "ВлаÑтивоÑті:" +msgstr "ВлаÑтивоÑті клаÑу:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Main Features:" -msgstr "МожливоÑті" +msgstr "ОÑновні можливоÑті:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Nodes and Classes:" -msgstr "Увімкнені клаÑи:" +msgstr "Вузли Ñ– клаÑи:" #: editor/editor_feature_profile.cpp msgid "File '%s' format is invalid, import aborted." @@ -1847,23 +1859,20 @@ msgid "Error saving profile to path: '%s'." msgstr "Помилка під Ñ‡Ð°Ñ Ñпроби зберегти профіль до каталогу: «%s»." #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Reset to Default" -msgstr "Відновити типові параметри" +msgstr "ПовернутиÑÑ Ð´Ð¾ типового" #: editor/editor_feature_profile.cpp msgid "Current Profile:" msgstr "Поточний профіль:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Create Profile" -msgstr "Витерти профіль" +msgstr "Створити профіль" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Remove Profile" -msgstr "Вилучити плитку" +msgstr "Вилучити профіль" #: editor/editor_feature_profile.cpp msgid "Available Profiles:" @@ -1883,18 +1892,18 @@ msgid "Export" msgstr "ЕкÑпортуваннÑ" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Configure Selected Profile:" -msgstr "Поточний профіль:" +msgstr "Ðалаштувати позначений профіль:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Extra Options:" -msgstr "Параметри клаÑу:" +msgstr "Додаткові параметри:" #: editor/editor_feature_profile.cpp msgid "Create or import a profile to edit available classes and properties." msgstr "" +"Створіть або імпортуйте профіль Ð´Ð»Ñ Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð¾Ñтупних клаÑів Ñ– " +"влаÑтивоÑтей." #: editor/editor_feature_profile.cpp msgid "New profile name:" @@ -1921,9 +1930,8 @@ msgid "Select Current Folder" msgstr "Вибрати поточну теку" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp -#, fuzzy msgid "File exists, overwrite?" -msgstr "Файл Ñ–Ñнує, перезапиÑати його?" +msgstr "Файл вже Ñ–Ñнує. ПерезапиÑати?" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp msgid "Select This Folder" @@ -2316,6 +2324,17 @@ msgid "New Window" msgstr "Ðове вікно" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "ОбертаєтьÑÑ, коли перемальовуєтьÑÑ Ð²Ñ–ÐºÐ½Ð¾ редактора." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Ðеможливо зберегти імпортовані реÑурÑи." @@ -2545,13 +2564,16 @@ msgid "" "The current scene has no root node, but %d modified external resource(s) " "were saved anyway." msgstr "" +"У поточної Ñцени немає кореневого вузла, але %d змінених зовнішніх реÑурÑів " +"було збережено попри це." #: editor/editor_node.cpp -#, fuzzy msgid "" "A root node is required to save the scene. You can add a root node using the " "Scene tree dock." -msgstr "Ð”Ð»Ñ Ñ‚Ð¾Ð³Ð¾, щоб можна було зберегти Ñцену, потрібен кореневий вузол." +msgstr "" +"Ð”Ð»Ñ Ð·Ð±ÐµÑ€ÐµÐ¶ÐµÐ½Ð½Ñ Ñцени потрібен кореневий вузол. Ви можете додати кореневий " +"вузол за допомогою бічної панелі ієрархії Ñцени." #: editor/editor_node.cpp msgid "Save Scene As..." @@ -2937,9 +2959,8 @@ msgid "Orphan Resource Explorer..." msgstr "ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð¾Ñиротілими реÑурÑами…" #: editor/editor_node.cpp -#, fuzzy msgid "Reload Current Project" -msgstr "Перейменувати проєкт" +msgstr "Перезавантажити поточний проєкт" #: editor/editor_node.cpp msgid "Quit to Project List" @@ -3100,13 +3121,12 @@ msgid "Help" msgstr "Довідка" #: editor/editor_node.cpp -#, fuzzy msgid "Online Documentation" -msgstr "Відкрити документацію" +msgstr "Ð”Ð¾ÐºÑƒÐ¼ÐµÐ½Ñ‚Ð°Ñ†Ñ–Ñ Ð² інтернеті" #: editor/editor_node.cpp msgid "Questions & Answers" -msgstr "" +msgstr "Ð—Ð°Ð¿Ð¸Ñ‚Ð°Ð½Ð½Ñ Ñ– відповіді" #: editor/editor_node.cpp msgid "Report a Bug" @@ -3114,7 +3134,7 @@ msgstr "Повідомити про ваду" #: editor/editor_node.cpp msgid "Suggest a Feature" -msgstr "" +msgstr "Запропонувати можливіÑть" #: editor/editor_node.cpp msgid "Send Docs Feedback" @@ -3125,9 +3145,8 @@ msgid "Community" msgstr "Спільнота" #: editor/editor_node.cpp -#, fuzzy msgid "About Godot" -msgstr "Про" +msgstr "Про Godot" #: editor/editor_node.cpp msgid "Support Godot Development" @@ -3179,10 +3198,6 @@ msgid "Save & Restart" msgstr "Зберегти Ñ– перезапуÑтити" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "ОбертаєтьÑÑ, коли перемальовуєтьÑÑ Ð²Ñ–ÐºÐ½Ð¾ редактора." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "Оновлювати неперервно" @@ -3225,14 +3240,12 @@ msgid "Manage Templates" msgstr "ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ ÑˆÐ°Ð±Ð»Ð¾Ð½Ð°Ð¼Ð¸" #: editor/editor_node.cpp -#, fuzzy msgid "Install from file" -msgstr "Ð’Ñтановити з файлу" +msgstr "Ð’Ñтановити з файла" #: editor/editor_node.cpp -#, fuzzy msgid "Select android sources file" -msgstr "Виберіть джерело Ñітки:" +msgstr "Виберіть файл початкових кодів android" #: editor/editor_node.cpp msgid "" @@ -3314,9 +3327,8 @@ msgid "Select" msgstr "Виділити" #: editor/editor_node.cpp -#, fuzzy msgid "Select Current" -msgstr "Вибрати поточну теку" +msgstr "Вибрати поточний" #: editor/editor_node.cpp msgid "Open 2D Editor" @@ -3351,9 +3363,8 @@ msgid "No sub-resources found." msgstr "Підлеглих реÑурÑів не знайдено." #: editor/editor_path.cpp -#, fuzzy msgid "Open a list of sub-resources." -msgstr "Підлеглих реÑурÑів не знайдено." +msgstr "Відкрити ÑпиÑок підлеглих реÑурÑів." #: editor/editor_plugin.cpp msgid "Creating Mesh Previews" @@ -3380,14 +3391,12 @@ msgid "Update" msgstr "Оновити" #: editor/editor_plugin_settings.cpp -#, fuzzy msgid "Version" -msgstr "ВерÑÑ–Ñ:" +msgstr "ВерÑÑ–Ñ" #: editor/editor_plugin_settings.cpp -#, fuzzy msgid "Author" -msgstr "Ðвтори" +msgstr "Ðвтор" #: editor/editor_plugin_settings.cpp #: editor/plugins/version_control_editor_plugin.cpp @@ -3400,14 +3409,12 @@ msgid "Measure:" msgstr "Вимірювати:" #: editor/editor_profiler.cpp -#, fuzzy msgid "Frame Time (ms)" -msgstr "Ð§Ð°Ñ ÐºÐ°Ð´Ñ€Ñƒ (Ñек)" +msgstr "Ð§Ð°Ñ ÐºÐ°Ð´Ñ€Ñƒ (мÑ)" #: editor/editor_profiler.cpp -#, fuzzy msgid "Average Time (ms)" -msgstr "Середній Ñ‡Ð°Ñ (Ñек)" +msgstr "Середній Ñ‡Ð°Ñ (мÑ)" #: editor/editor_profiler.cpp msgid "Frame %" @@ -3434,6 +3441,12 @@ msgid "" "functions called by that function.\n" "Use this to find individual functions to optimize." msgstr "" +"Включно: включає Ñ‡Ð°Ñ Ð· інших функцій, Ñкі викликає Ñ†Ñ Ñ„ÑƒÐ½ÐºÑ†Ñ–Ñ.\n" +"СкориÑтайтеÑÑ Ñ†Ð¸Ð¼ варіантом Ð´Ð»Ñ Ð²Ð¸ÑÐ²Ð»ÐµÐ½Ð½Ñ Ð²ÑƒÐ·ÑŒÐºÐ¸Ñ… міÑць.\n" +"\n" +"Цей об'єкт: визначати витрачений Ñ‡Ð°Ñ Ñƒ Ñамій функції, не в інших функціÑÑ…, " +"Ñкі Ñ†Ñ Ñ„ÑƒÐ½ÐºÑ†Ñ–Ñ Ð²Ð¸ÐºÐ»Ð¸ÐºÐ°Ñ”.\n" +"СкориÑтайтеÑÑ Ñ†Ð¸Ð¼ варіантом Ð´Ð»Ñ Ð¿Ð¾ÑˆÑƒÐºÑƒ окремих функцій Ð´Ð»Ñ Ð¾Ð¿Ñ‚Ð¸Ð¼Ñ–Ð·Ð°Ñ†Ñ–Ñ—." #: editor/editor_profiler.cpp msgid "Frame #:" @@ -3556,9 +3569,8 @@ msgid "Paste" msgstr "Ð’Ñтавити" #: editor/editor_resource_picker.cpp editor/property_editor.cpp -#, fuzzy msgid "Convert to %s" -msgstr "Перетворити на %s" +msgstr "Перетворити до %s" #: editor/editor_resource_picker.cpp editor/property_editor.cpp msgid "New %s" @@ -3608,10 +3620,9 @@ msgid "Did you forget the '_run' method?" msgstr "Ви забули метод '_run'?" #: editor/editor_spin_slider.cpp -#, fuzzy msgid "Hold %s to round to integers. Hold Shift for more precise changes." msgstr "" -"Утримуйте натиÑнутою Ctrl, щоб заокруглити до цілих. Утримуйте натиÑнутою " +"Утримуйте натиÑнутою %s, щоб заокруглити до цілих. Утримуйте натиÑнутою " "Shift, щоб зміни були точнішими." #: editor/editor_sub_scene.cpp @@ -3632,49 +3643,43 @@ msgstr "Імпортувати з вузла:" #: editor/export_template_manager.cpp msgid "Open the folder containing these templates." -msgstr "" +msgstr "Відкрити теку, Ñка міÑтить ці шаблони." #: editor/export_template_manager.cpp msgid "Uninstall these templates." -msgstr "" +msgstr "Вилучити ці шаблони." #: editor/export_template_manager.cpp -#, fuzzy msgid "There are no mirrors available." -msgstr "Ðемає файла «%s»." +msgstr "Ðемає доÑтупних дзеркал." #: editor/export_template_manager.cpp -#, fuzzy msgid "Retrieving the mirror list..." -msgstr "ÐžÑ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð´Ð·ÐµÑ€ÐºÐ°Ð», будь лаÑка, зачекайте..." +msgstr "Отримуємо ÑпиÑок дзеркал…" #: editor/export_template_manager.cpp msgid "Starting the download..." -msgstr "" +msgstr "Розпочинаємо Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð´Ð°Ð½Ð¸Ñ…â€¦" #: editor/export_template_manager.cpp msgid "Error requesting URL:" msgstr "Помилка під Ñ‡Ð°Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñƒ за такою адреÑою:" #: editor/export_template_manager.cpp -#, fuzzy msgid "Connecting to the mirror..." -msgstr "ÐŸÑ–Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ Ð´Ð¾ дзеркала..." +msgstr "Ð’Ñтановлюємо з'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ñ–Ð· дзеркалом…" #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't resolve the requested address." -msgstr "Ðеможливо розпізнати ім'Ñ Ñ…Ð¾Ñта:" +msgstr "Ðе вдалоÑÑ Ð²Ð¸Ð·Ð½Ð°Ñ‡Ð¸Ñ‚Ð¸ вузол за бажаною адреÑою." #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't connect to the mirror." -msgstr "Ðе вдалоÑÑ Ð¿Ñ–Ð´ÐºÐ»ÑŽÑ‡Ð¸Ñ‚Ð¸ÑÑ Ð´Ð¾ хоÑту:" +msgstr "Ðе вдалоÑÑ Ð²Ñтановити з'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ñ–Ð· дзеркалом." #: editor/export_template_manager.cpp -#, fuzzy msgid "No response from the mirror." -msgstr "Ðемає відповіді від хоÑта:" +msgstr "Ðемає відповіді від дзеркала." #: editor/export_template_manager.cpp #: editor/plugins/asset_library_editor_plugin.cpp @@ -3682,18 +3687,16 @@ msgid "Request failed." msgstr "Ðе вдалоÑÑ Ð²Ð¸ÐºÐ¾Ð½Ð°Ñ‚Ð¸ запит." #: editor/export_template_manager.cpp -#, fuzzy msgid "Request ended up in a redirect loop." -msgstr "Запит не вдавÑÑ, забагато перенаправлень" +msgstr "Спроба виконати запит завершилаÑÑ Ñ†Ð¸ÐºÐ»Ñ–Ñ‡Ð½Ð¸Ð¼ переÑпрÑмовуваннÑм." #: editor/export_template_manager.cpp -#, fuzzy msgid "Request failed:" -msgstr "Ðе вдалоÑÑ Ð²Ð¸ÐºÐ¾Ð½Ð°Ñ‚Ð¸ запит." +msgstr "Ðе вдалоÑÑ Ð²Ð¸ÐºÐ¾Ð½Ð°Ñ‚Ð¸ запит:" #: editor/export_template_manager.cpp msgid "Download complete; extracting templates..." -msgstr "" +msgstr "ÐžÑ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð´Ð°Ð½Ð¸Ñ… завершено; видобуваємо шаблони…" #: editor/export_template_manager.cpp msgid "Cannot remove temporary file:" @@ -3712,15 +3715,14 @@ msgid "Error getting the list of mirrors." msgstr "Помилка під Ñ‡Ð°Ñ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ ÑпиÑку дзеркал." #: editor/export_template_manager.cpp -#, fuzzy msgid "Error parsing JSON with the list of mirrors. Please report this issue!" msgstr "" -"Помилка під Ñ‡Ð°Ñ Ð¾Ð±Ñ€Ð¾Ð±ÐºÐ¸ JSON ÑпиÑку дзеркал. Будь лаÑка, повідомте про цю " -"ваду!" +"Помилка під Ñ‡Ð°Ñ Ñпроби обробити JSON зі ÑпиÑком дзеркал. Будь лаÑка, " +"повідомте розробникам про цю помилку!" #: editor/export_template_manager.cpp msgid "Best available mirror" -msgstr "" +msgstr "Ðайкраще доÑтупне дзеркало" #: editor/export_template_manager.cpp msgid "" @@ -3773,24 +3775,21 @@ msgid "SSL Handshake Error" msgstr "Помилка SSL РукоÑтиÑканнÑ" #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't open the export templates file." -msgstr "Ðеможливо відкрити ZIP-файл шаблону екÑпорту." +msgstr "Ðе вдалоÑÑ Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ð¸ файл шаблонів екÑпортуваннÑ." #: editor/export_template_manager.cpp -#, fuzzy msgid "Invalid version.txt format inside the export templates file: %s." -msgstr "Ðеправильний формат version.txt у шаблонах: %s." +msgstr "" +"Ðекоректне Ñ„Ð¾Ñ€Ð¼Ð°Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ version.txt у файлі шаблонів екÑпортуваннÑ: %s." #: editor/export_template_manager.cpp -#, fuzzy msgid "No version.txt found inside the export templates file." -msgstr "Файл version.txt не знайдено у шаблонах." +msgstr "У файлі шаблонів екÑÐ¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð½ÐµÐ¼Ð°Ñ” version.txt." #: editor/export_template_manager.cpp -#, fuzzy msgid "Error creating path for extracting templates:" -msgstr "Помилка ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ ÑˆÐ»Ñху Ð´Ð»Ñ ÑˆÐ°Ð±Ð»Ð¾Ð½Ñ–Ð²:" +msgstr "Помилка ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ ÑˆÐ»Ñху Ð´Ð»Ñ Ð²Ð¸Ð´Ð¾Ð±ÑƒÑ‚Ð¸Ñ… шаблонів:" #: editor/export_template_manager.cpp msgid "Extracting Export Templates" @@ -3801,9 +3800,8 @@ msgid "Importing:" msgstr "ІмпортуваннÑ:" #: editor/export_template_manager.cpp -#, fuzzy msgid "Remove templates for the version '%s'?" -msgstr "Вилучити верÑÑ–ÑŽ шаблону '%s'?" +msgstr "Вилучити шаблони Ð´Ð»Ñ Ð²ÐµÑ€ÑÑ–Ñ— «%s»?" #: editor/export_template_manager.cpp msgid "Uncompressing Android Build Sources" @@ -3820,57 +3818,65 @@ msgstr "Поточна верÑÑ–Ñ:" #: editor/export_template_manager.cpp msgid "Export templates are missing. Download them or install from a file." msgstr "" +"Ðе виÑтачає шаблонів екÑпортуваннÑ. Отримайте Ñ—Ñ… або вÑтановіть з файла." #: editor/export_template_manager.cpp msgid "Export templates are installed and ready to be used." -msgstr "" +msgstr "Шаблони екÑÐ¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð²Ñтановлено Ñ– приготовано до викориÑтаннÑ." #: editor/export_template_manager.cpp -#, fuzzy msgid "Open Folder" -msgstr "Відкрити файл" +msgstr "Відкрити теку" #: editor/export_template_manager.cpp msgid "Open the folder containing installed templates for the current version." -msgstr "" +msgstr "Відкрити теку, що міÑтить вÑтановлені шаблони Ð´Ð»Ñ Ð¿Ð¾Ñ‚Ð¾Ñ‡Ð½Ð¾Ñ— верÑÑ–Ñ—." #: editor/export_template_manager.cpp msgid "Uninstall" msgstr "Видалити" #: editor/export_template_manager.cpp -#, fuzzy msgid "Uninstall templates for the current version." -msgstr "Початкове Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð´Ð»Ñ Ð»Ñ–Ñ‡Ð¸Ð»ÑŒÐ½Ð¸ÐºÐ°" +msgstr "Вилучити шаблони Ð´Ð»Ñ Ð¿Ð¾Ñ‚Ð¾Ñ‡Ð½Ð¾Ñ— верÑÑ–Ñ—." #: editor/export_template_manager.cpp -#, fuzzy msgid "Download from:" -msgstr "Помилка завантаженнÑ" +msgstr "Джерело отриманнÑ:" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "ЗапуÑтити в браузері" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "Помилка копіюваннÑ" #: editor/export_template_manager.cpp msgid "Download and Install" -msgstr "" +msgstr "Отримати Ñ– вÑтановити" #: editor/export_template_manager.cpp msgid "" "Download and install templates for the current version from the best " "possible mirror." msgstr "" +"Отримати Ñ– вÑтановити шаблони Ð´Ð»Ñ Ð¿Ð¾Ñ‚Ð¾Ñ‡Ð½Ð¾Ñ— верÑÑ–Ñ— із найкращого можливого " +"дзеркала." #: editor/export_template_manager.cpp msgid "Official export templates aren't available for development builds." msgstr "Ð”Ð»Ñ Ñ‚ÐµÑтових збірок не передбачено офіційних шаблонів екÑпортуваннÑ." #: editor/export_template_manager.cpp -#, fuzzy msgid "Install from File" -msgstr "Ð’Ñтановити з файлу" +msgstr "Ð’Ñтановити з файла" #: editor/export_template_manager.cpp -#, fuzzy msgid "Install templates from a local file." -msgstr "Імпортувати шаблони з ZIP-файлу" +msgstr "Ð’Ñтановити шаблони з локального файла." #: editor/export_template_manager.cpp editor/find_in_files.cpp #: editor/progress_dialog.cpp scene/gui/dialogs.cpp @@ -3878,19 +3884,16 @@ msgid "Cancel" msgstr "СкаÑувати" #: editor/export_template_manager.cpp -#, fuzzy msgid "Cancel the download of the templates." -msgstr "Ðеможливо відкрити ZIP-файл шаблону екÑпорту." +msgstr "СкаÑувати Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ ÑˆÐ°Ð±Ð»Ð¾Ð½Ñ–Ð²." #: editor/export_template_manager.cpp -#, fuzzy msgid "Other Installed Versions:" -msgstr "Ð’Ñтановлені верÑÑ–Ñ—:" +msgstr "Інші вÑтановлені верÑÑ–Ñ—:" #: editor/export_template_manager.cpp -#, fuzzy msgid "Uninstall Template" -msgstr "Видалити" +msgstr "Вилучити шаблон" #: editor/export_template_manager.cpp msgid "Select Template File" @@ -3905,6 +3908,8 @@ msgid "" "The templates will continue to download.\n" "You may experience a short editor freeze when they finish." msgstr "" +"ÐžÑ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ ÑˆÐ°Ð±Ð»Ð¾Ð½Ñ–Ð² буде продовжено.\n" +"Під Ñ‡Ð°Ñ Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ Ð¼Ð¾Ð¶Ð»Ð¸Ð²Ðµ тимчаÑове «замерзаннÑ» редактора." #: editor/filesystem_dock.cpp msgid "Favorites" @@ -4052,35 +4057,32 @@ msgid "Collapse All" msgstr "Згорнути вÑе" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort files" -msgstr "Шукати файли" +msgstr "УпорÑдкувати файли" #: editor/filesystem_dock.cpp msgid "Sort by Name (Ascending)" -msgstr "" +msgstr "УпорÑдкувати за назвою (зроÑтаннÑ)" #: editor/filesystem_dock.cpp msgid "Sort by Name (Descending)" -msgstr "" +msgstr "УпорÑдкувати за назвою (ÑпаданнÑ)" #: editor/filesystem_dock.cpp msgid "Sort by Type (Ascending)" -msgstr "" +msgstr "УпорÑдкувати за типом (зроÑтаннÑ)" #: editor/filesystem_dock.cpp msgid "Sort by Type (Descending)" -msgstr "" +msgstr "УпорÑдкувати за типом (ÑпаданнÑ)" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort by Last Modified" -msgstr "ВоÑтаннє змінено" +msgstr "УпорÑдкувати за оÑтаннім внеÑеннÑм змін" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort by First Modified" -msgstr "ВоÑтаннє змінено" +msgstr "УпорÑдкувати за початковим внеÑеннÑм змін" #: editor/filesystem_dock.cpp msgid "Duplicate..." @@ -4092,7 +4094,7 @@ msgstr "Перейменувати..." #: editor/filesystem_dock.cpp msgid "Focus the search box" -msgstr "" +msgstr "ФокуÑувати поле Ð´Ð»Ñ Ð¿Ð¾ÑˆÑƒÐºÑƒ" #: editor/filesystem_dock.cpp msgid "Previous Folder/File" @@ -4400,14 +4402,12 @@ msgid "Failed to load resource." msgstr "Ðе вдалоÑÑ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶Ð¸Ñ‚Ð¸ реÑурÑ." #: editor/inspector_dock.cpp -#, fuzzy msgid "Copy Properties" -msgstr "ВлаÑтивоÑті" +msgstr "Копіювати влаÑтивоÑті" #: editor/inspector_dock.cpp -#, fuzzy msgid "Paste Properties" -msgstr "ВлаÑтивоÑті" +msgstr "Ð’Ñтавити влаÑтивоÑті" #: editor/inspector_dock.cpp msgid "Make Sub-Resources Unique" @@ -4432,23 +4432,20 @@ msgid "Save As..." msgstr "Зберегти Ñк..." #: editor/inspector_dock.cpp -#, fuzzy msgid "Extra resource options." -msgstr "Ðе в реÑурÑному шлÑху." +msgstr "Додаткові параметри реÑурÑу." #: editor/inspector_dock.cpp -#, fuzzy msgid "Edit Resource from Clipboard" -msgstr "Редагувати буфер реÑурÑів" +msgstr "Редагувати реÑÑƒÑ€Ñ Ð· буфера обміну" #: editor/inspector_dock.cpp msgid "Copy Resource" msgstr "Копіювати реÑурÑ" #: editor/inspector_dock.cpp -#, fuzzy msgid "Make Resource Built-In" -msgstr "Зробити вбудованим" +msgstr "Зробити реÑÑƒÑ€Ñ Ð²Ð±ÑƒÐ´Ð¾Ð²Ð°Ð½Ð¸Ð¼" #: editor/inspector_dock.cpp msgid "Go to the previous edited object in history." @@ -4463,9 +4460,8 @@ msgid "History of recently edited objects." msgstr "ІÑÑ‚Ð¾Ñ€Ñ–Ñ Ð½ÐµÑ‰Ð¾Ð´Ð°Ð²Ð½Ð¾ відредагованих об'єктів." #: editor/inspector_dock.cpp -#, fuzzy msgid "Open documentation for this object." -msgstr "Відкрити документацію" +msgstr "Відкрити документацію Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ об'єкта." #: editor/inspector_dock.cpp editor/scene_tree_dock.cpp msgid "Open Documentation" @@ -4476,9 +4472,8 @@ msgid "Filter properties" msgstr "Фільтрувати влаÑтивоÑті" #: editor/inspector_dock.cpp -#, fuzzy msgid "Manage object properties." -msgstr "ВлаÑтивоÑті об'єкта." +msgstr "Керувати влаÑтивоÑÑ‚Ñми об'єкта." #: editor/inspector_dock.cpp msgid "Changes may be lost!" @@ -4726,9 +4721,8 @@ msgid "Blend:" msgstr "Змішувати:" #: editor/plugins/animation_blend_tree_editor_plugin.cpp -#, fuzzy msgid "Parameter Changed:" -msgstr "Змінено параметр" +msgstr "Змінено параметр:" #: editor/plugins/animation_blend_tree_editor_plugin.cpp #: editor/plugins/animation_tree_player_editor_plugin.cpp @@ -5460,11 +5454,11 @@ msgstr "Ð’Ñе" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Search templates, projects, and demos" -msgstr "" +msgstr "Шукати шаблони, проєкти та демонÑтрації" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Search assets (excluding templates, projects, and demos)" -msgstr "" +msgstr "Пошук пакунків (із виключеннÑм шаблонів, проєктів та демонÑтрацій)" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Import..." @@ -5508,7 +5502,7 @@ msgstr "ZIP файл реÑурÑів" #: editor/plugins/audio_stream_editor_plugin.cpp msgid "Audio Preview Play/Pause" -msgstr "" +msgstr "ПуÑк/Пауза проÑÐ»ÑƒÑ…Ð¾Ð²ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð²ÑƒÐºÑƒ" #: editor/plugins/baked_lightmap_editor_plugin.cpp msgid "" @@ -5769,13 +5763,12 @@ msgstr "Змінити прив'Ñзки" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "" "Project Camera Override\n" "Overrides the running project's camera with the editor viewport camera." msgstr "" -"ÐŸÐµÑ€ÐµÐ²Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ ÐºÐ°Ð¼ÐµÑ€Ð¸ гри\n" -"Замінює камеру гри камерою видимої облаÑті редактора." +"ÐŸÐµÑ€ÐµÐ²Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ ÐºÐ°Ð¼ÐµÑ€Ð¸ проєкту\n" +"Замінює поточну камеру проєкту камерою видимої облаÑті редактора." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp @@ -5784,6 +5777,9 @@ msgid "" "No project instance running. Run the project from the editor to use this " "feature." msgstr "" +"ÐŸÐµÑ€ÐµÐ²Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ ÐºÐ°Ð¼ÐµÑ€Ð¸ проєкту\n" +"Ðемає запущеного екземплÑра проєкту. ЗапуÑтіть проєкт з вікна редактора, щоб " +"ÑкориÑтатиÑÑ Ñ†Ñ–Ñ”ÑŽ можливіÑтю." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp @@ -5851,31 +5847,27 @@ msgstr "Режим виділеннÑ" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Drag: Rotate selected node around pivot." -msgstr "Вилучити позначений вузол або перехід." +msgstr "ПеретÑгуваннÑ: обертати позначений вузол навколо опорної точки." #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Alt+Drag: Move selected node." -msgstr "Alt+ПеретÑгнути: переміÑтити" +msgstr "Alt+ПеретÑгнути: переміÑтити позначений вузол." #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "V: Set selected node's pivot position." -msgstr "Вилучити позначений вузол або перехід." +msgstr "V: вÑтановити позицію опорної точки позначеного вузла." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Alt+RMB: Show list of all nodes at position clicked, including locked." msgstr "" -"Показати ÑпиÑок уÑÑ–Ñ… об'єктів, натиÑнутих на позицію\n" -"(так Ñамо, Ñк Ðльт+ПКМ у режимі вибору)." +"Alt+ПКМ: показати ÑпиÑок уÑÑ–Ñ… вузлів у позиції клацаннÑ, включно із " +"заблокованими." #: editor/plugins/canvas_item_editor_plugin.cpp msgid "RMB: Add node at position clicked." -msgstr "" +msgstr "ПКМ: додати вузол у позиції клацаннÑ." #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp @@ -6113,14 +6105,12 @@ msgid "Clear Pose" msgstr "ОчиÑтити позу" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Add Node Here" -msgstr "Додати вузол" +msgstr "Додати вузол тут" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Instance Scene Here" -msgstr "Сцени екземплÑра" +msgstr "ЕкземплÑÑ€ Ñцени тут" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Multiply grid step by 2" @@ -6136,49 +6126,43 @@ msgstr "ÐŸÐ°Ð½Ð¾Ñ€Ð°Ð¼ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 3.125%" -msgstr "" +msgstr "МаÑштаб у 3,125%" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 6.25%" -msgstr "" +msgstr "МаÑштаб у 6,25%" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 12.5%" -msgstr "" +msgstr "МаÑштаб у 12,5%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 25%" -msgstr "ЗменшеннÑ" +msgstr "МаÑштаб у 25%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 50%" -msgstr "ЗменшеннÑ" +msgstr "МаÑштаб у 50%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 100%" -msgstr "ЗменшеннÑ" +msgstr "МаÑштаб у 100%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 200%" -msgstr "ЗменшеннÑ" +msgstr "МаÑштаб у 200%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 400%" -msgstr "ЗменшеннÑ" +msgstr "МаÑштаб у 400%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 800%" -msgstr "ЗменшеннÑ" +msgstr "МаÑштаб у 800%" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 1600%" -msgstr "" +msgstr "МаÑштаб у 1600%" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Add %s" @@ -6424,9 +6408,8 @@ msgid "Couldn't create a single convex collision shape." msgstr "Ðе вдалоÑÑ Ñтворити єдину опуклу форму зіткненнÑ." #: editor/plugins/mesh_instance_editor_plugin.cpp -#, fuzzy msgid "Create Simplified Convex Shape" -msgstr "Створити єдину опуклу форму" +msgstr "Створити Ñпрощену опуклу форму" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Create Single Convex Shape" @@ -6461,9 +6444,8 @@ msgid "No mesh to debug." msgstr "Ðемає Ñітки Ð´Ð»Ñ Ð½Ð°Ð»Ð°Ð³Ð¾Ð´Ð¶ÐµÐ½Ð½Ñ." #: editor/plugins/mesh_instance_editor_plugin.cpp -#, fuzzy msgid "Mesh has no UV in layer %d." -msgstr "Модель не має UV на цьому шарі" +msgstr "Сітка не має UV у шарі %d." #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "MeshInstance lacks a Mesh!" @@ -6528,9 +6510,8 @@ msgstr "" "Цей найшвидший (але найменш точний) варіант Ð´Ð»Ñ Ð²Ð¸ÑÐ²Ð»ÐµÐ½Ð½Ñ Ð·Ñ–Ñ‚ÐºÐ½ÐµÐ½ÑŒ." #: editor/plugins/mesh_instance_editor_plugin.cpp -#, fuzzy msgid "Create Simplified Convex Collision Sibling" -msgstr "Створити єдину опуклу облаÑть зіткненнÑ" +msgstr "Створити Ñпрощену опуклу облаÑть зіткненнÑ" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "" @@ -6538,20 +6519,23 @@ msgid "" "This is similar to single collision shape, but can result in a simpler " "geometry in some cases, at the cost of accuracy." msgstr "" +"Створює Ñпрощену опуклу форму зіткненнÑ.\n" +"Це Ñхоже на єдину форму зіткненнÑ, але може призвеÑти у деÑких випадках до " +"проÑтішої геометрії ціною точноÑті." #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Create Multiple Convex Collision Siblings" msgstr "Створити декілька опуклих облаÑтей зіткненнÑ" #: editor/plugins/mesh_instance_editor_plugin.cpp -#, fuzzy msgid "" "Creates a polygon-based collision shape.\n" "This is a performance middle-ground between a single convex collision and a " "polygon-based collision." msgstr "" "Створює заÑновану на багатокутниках форму зіткненнÑ.\n" -"Цей проміжний за швидкіÑтю варіант між наведеними вище двома варіантами." +"Цей проміжний за швидкодією варіант між єдиною опуклою формою Ð·Ñ–Ñ‚ÐºÐ½ÐµÐ½Ð½Ñ Ñ– " +"заÑнованою на багатокутниках формою зіткненнÑ." #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Create Outline Mesh..." @@ -7195,24 +7179,20 @@ msgid "ResourcePreloader" msgstr "Передзавантажувач реÑурÑів" #: editor/plugins/room_manager_editor_plugin.cpp -#, fuzzy msgid "Flip Portals" -msgstr "Віддзеркалити горизонтально" +msgstr "Віддзеркалити портали" #: editor/plugins/room_manager_editor_plugin.cpp -#, fuzzy msgid "Room Generate Points" -msgstr "КількіÑть генерованих точок:" +msgstr "Створити точки кімнати" #: editor/plugins/room_manager_editor_plugin.cpp -#, fuzzy msgid "Generate Points" -msgstr "КількіÑть генерованих точок:" +msgstr "Створити точки" #: editor/plugins/room_manager_editor_plugin.cpp -#, fuzzy msgid "Flip Portal" -msgstr "Віддзеркалити горизонтально" +msgstr "Віддзеркалити портал" #: editor/plugins/root_motion_editor_plugin.cpp msgid "AnimationTree has no path set to an AnimationPlayer" @@ -7778,20 +7758,17 @@ msgid "None" msgstr "Ðемає" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Rotate" -msgstr "Режим повороту" +msgstr "Обертати" #. TRANSLATORS: This refers to the movement that changes the position of an object. #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Translate" -msgstr "ПеренеÑеннÑ:" +msgstr "ПереÑунути" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Scale" -msgstr "МаÑштаб:" +msgstr "МаÑштаб" #: editor/plugins/spatial_editor_plugin.cpp msgid "Scaling: " @@ -7814,52 +7791,44 @@ msgid "Animation Key Inserted." msgstr "Ð’Ñтавлено ключ анімації." #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Pitch:" -msgstr "ХилитаннÑ" +msgstr "Тон:" #: editor/plugins/spatial_editor_plugin.cpp msgid "Yaw:" -msgstr "" +msgstr "РиÑканнÑ:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Size:" -msgstr "Розмір: " +msgstr "Розмір:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Objects Drawn:" -msgstr "Ðамальовано об'єктів" +msgstr "Ðамальовано об'єктів:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Material Changes:" -msgstr "Зміни матеріалу" +msgstr "Зміни матеріалу:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Shader Changes:" -msgstr "Зміни шейдерів" +msgstr "Зміни шейдерів:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Surface Changes:" -msgstr "Зміни поверхонь" +msgstr "Зміни поверхонь:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Draw Calls:" -msgstr "Виклики заÑобу малюваннÑ" +msgstr "Ðамалювати виклики:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Vertices:" -msgstr "Вершини" +msgstr "Вершини:" #: editor/plugins/spatial_editor_plugin.cpp msgid "FPS: %d (%s ms)" -msgstr "" +msgstr "ЧаÑтота кадрів: %d (%s мÑ)" #: editor/plugins/spatial_editor_plugin.cpp msgid "Top View." @@ -8014,9 +7983,8 @@ msgid "Freelook Slow Modifier" msgstr "Модифікатор швидкоÑті довільного оглÑду" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Toggle Camera Preview" -msgstr "Змінити розмір камери" +msgstr "Перемкнути попередній переглÑд камери" #: editor/plugins/spatial_editor_plugin.cpp msgid "View Rotation Locked" @@ -8039,9 +8007,8 @@ msgstr "" "грі." #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Convert Rooms" -msgstr "Перетворити на %s" +msgstr "Перетворити кімнати" #: editor/plugins/spatial_editor_plugin.cpp msgid "XForm Dialog" @@ -8062,7 +8029,6 @@ msgstr "" "Ðапівзакрите око: Gizmo Ñ” також видимим крізь непрозорі поверхні («рентген»)." #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Snap Nodes to Floor" msgstr "Приліпити вузли до підлоги" @@ -8080,7 +8046,7 @@ msgstr "За допомогою функції прив'Ñзки" #: editor/plugins/spatial_editor_plugin.cpp msgid "Converts rooms for portal culling." -msgstr "" +msgstr "Перетворює кімнати Ð´Ð»Ñ Ð²Ñ–Ð´Ð±Ñ€Ð°ÐºÐ¾Ð²ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ð¾Ñ€Ñ‚Ð°Ð»Ñƒ." #: editor/plugins/spatial_editor_plugin.cpp msgid "Bottom View" @@ -8176,9 +8142,8 @@ msgid "View Grid" msgstr "ПереглÑд ґратки" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "View Portal Culling" -msgstr "Параметри панелі переглÑду" +msgstr "ПереглÑнути Ð²Ñ–Ð´Ð±Ñ€Ð°ÐºÐ¾Ð²ÑƒÐ²Ð°Ð½Ð½Ñ Portal" #: editor/plugins/spatial_editor_plugin.cpp #: modules/gridmap/grid_map_editor_plugin.cpp @@ -8500,221 +8465,196 @@ msgid "TextureRegion" msgstr "TextureRegion" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Colors" -msgstr "Колір" +msgstr "Кольори" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Fonts" -msgstr "Шрифт" +msgstr "Шрифти" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Icons" -msgstr "Піктограма" +msgstr "Піктограми" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Styleboxes" -msgstr "Style Box" +msgstr "Стильові панелі" #: editor/plugins/theme_editor_plugin.cpp msgid "{num} color(s)" -msgstr "" +msgstr "{num} кольорів" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No colors found." -msgstr "Підлеглих реÑурÑів не знайдено." +msgstr "Кольорів не знайдено." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "{num} constant(s)" -msgstr "КонÑтанти" +msgstr "{num} Ñталих" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No constants found." -msgstr "Сталий колір." +msgstr "Сталих не знайдено." #: editor/plugins/theme_editor_plugin.cpp msgid "{num} font(s)" -msgstr "" +msgstr "{num} шрифтів" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No fonts found." -msgstr "Ðе знайдено!" +msgstr "Шрифтів не знайдено." #: editor/plugins/theme_editor_plugin.cpp msgid "{num} icon(s)" -msgstr "" +msgstr "{num} піктограм" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No icons found." -msgstr "Ðе знайдено!" +msgstr "Піктограм не знайдено." #: editor/plugins/theme_editor_plugin.cpp msgid "{num} stylebox(es)" -msgstr "" +msgstr "{num} панелей Ñтилів" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No styleboxes found." -msgstr "Підлеглих реÑурÑів не знайдено." +msgstr "Панелей Ñтилів не знайдено." #: editor/plugins/theme_editor_plugin.cpp msgid "{num} currently selected" -msgstr "" +msgstr "{num} зараз позначених" #: editor/plugins/theme_editor_plugin.cpp msgid "Nothing was selected for the import." -msgstr "" +msgstr "Ðічого не позначено Ð´Ð»Ñ Ñ–Ð¼Ð¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Importing Theme Items" -msgstr "Імпортувати тему" +msgstr "Ð†Ð¼Ð¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð°Ð¿Ð¸Ñів теми" #: editor/plugins/theme_editor_plugin.cpp msgid "Importing items {n}/{n}" -msgstr "" +msgstr "Ð†Ð¼Ð¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð°Ð¿Ð¸Ñів, {n} з {n}" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Updating the editor" -msgstr "Вийти з редактора?" +msgstr "ÐžÐ½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¾Ñ€Ð°" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Finalizing" -msgstr "Ðналіз" +msgstr "ЗакріпленнÑ" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Filter:" -msgstr "Фільтри:" +msgstr "Фільтр:" #: editor/plugins/theme_editor_plugin.cpp msgid "With Data" -msgstr "" +msgstr "З даними" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select by data type:" -msgstr "Виберіть вузол" +msgstr "Вибір за типом даних:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible color items." -msgstr "Виберіть поділ Ð´Ð»Ñ Ð¹Ð¾Ð³Ð¾ витираннÑ." +msgstr "Вибрати уÑÑ– видимі запиÑи кольорів." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible color items and their data." -msgstr "" +msgstr "Вибрати уÑÑ– видимі запиÑи кольорів та їхні дані." #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all visible color items." -msgstr "" +msgstr "СкаÑувати вибір уÑÑ–Ñ… видимих запиÑів кольорів." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible constant items." -msgstr "Спочатку виберіть елемент параметра!" +msgstr "Вибрати уÑÑ– видимі запиÑи Ñталих." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible constant items and their data." -msgstr "" +msgstr "Вибрати уÑÑ– запиÑи уÑÑ–Ñ… видимих Ñталих та їхні дані." #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all visible constant items." -msgstr "" +msgstr "СкаÑувати вибір уÑÑ–Ñ… видимих запиÑів Ñталих." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible font items." -msgstr "Спочатку виберіть елемент параметра!" +msgstr "Вибрати уÑÑ– видимі запиÑи шрифтів." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible font items and their data." -msgstr "" +msgstr "Вибрати уÑÑ– видимі запиÑи шрифтів та їхні дані." #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all visible font items." -msgstr "" +msgstr "СкаÑувати вибір уÑÑ–Ñ… видимих запиÑів шрифтів." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible icon items." -msgstr "Спочатку виберіть елемент параметра!" +msgstr "Вибрати уÑÑ– видимі запиÑи піктограм." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible icon items and their data." -msgstr "Спочатку виберіть елемент параметра!" +msgstr "Вибрати уÑÑ– видимі запиÑи піктограм та їхні дані." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Deselect all visible icon items." -msgstr "Спочатку виберіть елемент параметра!" +msgstr "СкаÑувати вибір уÑÑ–Ñ… видимих запиÑів піктограм." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible stylebox items." -msgstr "" +msgstr "Вибрати уÑÑ– видимі запиÑи панелей Ñтилів." #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible stylebox items and their data." -msgstr "" +msgstr "Вибрати уÑÑ– видимі запиÑи панелей Ñтилів та їхні дані." #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all visible stylebox items." -msgstr "" +msgstr "СкаÑувати вибір уÑÑ–Ñ… видимих запиÑів панелей Ñтилів." #: editor/plugins/theme_editor_plugin.cpp msgid "" "Caution: Adding icon data may considerably increase the size of your Theme " "resource." msgstr "" +"ПопередженнÑ: Ð´Ð¾Ð´Ð°Ð²Ð°Ð½Ð½Ñ Ð´Ð°Ð½Ð¸Ñ… піктограм може значно збільшити розмір вашого " +"реÑурÑу теми." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Collapse types." -msgstr "Згорнути вÑе" +msgstr "Згорнути типи." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Expand types." -msgstr "Розгорнути вÑе" +msgstr "Розгорнути типи." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all Theme items." -msgstr "Виберіть файл шаблону" +msgstr "Вибрати уÑÑ– запиÑи тем." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select With Data" -msgstr "Виберіть пункти" +msgstr "Вибрати з даними" #: editor/plugins/theme_editor_plugin.cpp msgid "Select all Theme items with item data." -msgstr "" +msgstr "Вибрати уÑÑ– запиÑи тем із даними запиÑу." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Deselect All" -msgstr "Виділити вÑе" +msgstr "ЗнÑти Ð¿Ð¾Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð· уÑÑ–Ñ…" #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all Theme items." -msgstr "" +msgstr "СкаÑувати вибір уÑÑ–Ñ… запиÑів тем." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Import Selected" -msgstr "Імпортувати Ñцену" +msgstr "Імпортувати позначене" #: editor/plugins/theme_editor_plugin.cpp msgid "" @@ -8722,271 +8662,247 @@ msgid "" "closing this window.\n" "Close anyway?" msgstr "" +"Ðа панелі Ñ–Ð¼Ð¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð°Ð¿Ð¸Ñів позначено деÑкі запиÑи. Якщо закрити це " +"вікно, Ð¿Ð¾Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð±ÑƒÐ´Ðµ знÑто.\n" +"Закрити вікно попри це?" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All Color Items" -msgstr "Вилучити уÑÑ– елементи" +msgstr "Вилучити уÑÑ– запиÑи кольорів" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Item" -msgstr "Вилучити елемент" +msgstr "Перейменувати запиÑ" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All Constant Items" -msgstr "Вилучити уÑÑ– елементи" +msgstr "Вилучити уÑÑ– запиÑи Ñталих" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All Font Items" -msgstr "Вилучити уÑÑ– елементи" +msgstr "Вилучити уÑÑ– запиÑи шрифтів" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All Icon Items" -msgstr "Вилучити уÑÑ– елементи" +msgstr "Вилучити уÑÑ– запиÑи піктограм" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All StyleBox Items" -msgstr "Вилучити уÑÑ– елементи" +msgstr "Вилучити уÑÑ– запиÑи панелей Ñтилів" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Color Item" -msgstr "Додати елементи клаÑу" +msgstr "Додати Ð·Ð°Ð¿Ð¸Ñ ÐºÐ¾Ð»ÑŒÐ¾Ñ€Ñƒ" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Constant Item" -msgstr "Додати елементи клаÑу" +msgstr "Додати Ð·Ð°Ð¿Ð¸Ñ Ñталої" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Font Item" -msgstr "Додати елемент" +msgstr "Додати Ð·Ð°Ð¿Ð¸Ñ ÑˆÑ€Ð¸Ñ„Ñ‚Ñƒ" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Icon Item" -msgstr "Додати елемент" +msgstr "Додати Ð·Ð°Ð¿Ð¸Ñ Ð¿Ñ–ÐºÑ‚Ð¾Ð³Ñ€Ð°Ð¼Ð¸" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Stylebox Item" -msgstr "Додати уÑÑ– елементи" +msgstr "Додати Ð·Ð°Ð¿Ð¸Ñ Ð¿Ð°Ð½ÐµÐ»Ñ– Ñтилів" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Color Item" -msgstr "Вилучити елементи клаÑу" +msgstr "Перейменувати Ð·Ð°Ð¿Ð¸Ñ ÐºÐ¾Ð»ÑŒÐ¾Ñ€Ñƒ" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Constant Item" -msgstr "Вилучити елементи клаÑу" +msgstr "Перейменувати Ð·Ð°Ð¿Ð¸Ñ Ñталої" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Font Item" -msgstr "Перейменувати вузол" +msgstr "Перейменувати Ð·Ð°Ð¿Ð¸Ñ ÑˆÑ€Ð¸Ñ„Ñ‚Ñƒ" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Icon Item" -msgstr "Перейменувати вузол" +msgstr "Перейменувати Ð·Ð°Ð¿Ð¸Ñ Ð¿Ñ–ÐºÑ‚Ð¾Ð³Ñ€Ð°Ð¼Ð¸" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Stylebox Item" -msgstr "Вилучити вибраний елемент" +msgstr "Перейменувати Ð·Ð°Ð¿Ð¸Ñ Ð¿Ð°Ð½ÐµÐ»Ñ– Ñтилів" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Invalid file, not a Theme resource." -msgstr "ÐеприпуÑтимий файл, це не ÐºÐ¾Ð¼Ð¿Ð¾Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Ð°ÑƒÐ´Ñ–Ð¾-шини." +msgstr "Ðекоректний файл. Файл не Ñ” реÑурÑом теми." #: editor/plugins/theme_editor_plugin.cpp msgid "Invalid file, same as the edited Theme resource." -msgstr "" +msgstr "Ðекоректний файл. Файл збігаєтьÑÑ Ñ–Ð· редагованим реÑурÑом теми." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Manage Theme Items" -msgstr "ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ ÑˆÐ°Ð±Ð»Ð¾Ð½Ð°Ð¼Ð¸" +msgstr "ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð°Ð¿Ð¸Ñами теми" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Edit Items" -msgstr "Редагований елемент" +msgstr "Редагувати запиÑи" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Types:" -msgstr "Тип:" +msgstr "Типи:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Type:" -msgstr "Тип:" +msgstr "Додати тип:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Item:" -msgstr "Додати елемент" +msgstr "Додати запиÑ:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add StyleBox Item" -msgstr "Додати уÑÑ– елементи" +msgstr "Додати Ð·Ð°Ð¿Ð¸Ñ Ð¿Ð°Ð½ÐµÐ»Ñ– Ñтилів" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove Items:" -msgstr "Вилучити елемент" +msgstr "Вилучити запиÑи:" #: editor/plugins/theme_editor_plugin.cpp msgid "Remove Class Items" -msgstr "Вилучити елементи клаÑу" +msgstr "Вилучити запиÑи клаÑу" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove Custom Items" -msgstr "Вилучити елементи клаÑу" +msgstr "Вилучити нетипові запиÑи" #: editor/plugins/theme_editor_plugin.cpp msgid "Remove All Items" msgstr "Вилучити уÑÑ– елементи" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Theme Item" -msgstr "Тема елементів ГІК" +msgstr "Додати Ð·Ð°Ð¿Ð¸Ñ Ñ‚ÐµÐ¼Ð¸" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Old Name:" -msgstr "Ім'Ñ Ð’ÑƒÐ·Ð»Ð°:" +msgstr "Стара назва:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Import Items" -msgstr "Імпортувати тему" +msgstr "Імпортовані пункти" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Default Theme" -msgstr "Типовий" +msgstr "Типова тема" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Editor Theme" -msgstr "Редагувати тему" +msgstr "Тема редактора" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select Another Theme Resource:" -msgstr "Вилучити реÑурÑ" +msgstr "Виберіть реÑÑƒÑ€Ñ Ñ–Ð½ÑˆÐ¾Ñ— теми:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Another Theme" -msgstr "Імпортувати тему" +msgstr "Інша тема" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Confirm Item Rename" -msgstr "Перейменувати доріжку" +msgstr "Підтвердити Ð¿ÐµÑ€ÐµÐ¹Ð¼ÐµÐ½ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð°Ð¿Ð¸Ñу" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Cancel Item Rename" -msgstr "Пакетне перейменуваннÑ" +msgstr "СкаÑувати Ð¿ÐµÑ€ÐµÐ¹Ð¼ÐµÐ½ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð°Ð¿Ð¸Ñу" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Override Item" -msgstr "ПеревизначеннÑ" +msgstr "Перевизначити запиÑ" #: editor/plugins/theme_editor_plugin.cpp msgid "Unpin this StyleBox as a main style." -msgstr "" +msgstr "Відшпилити цю панель Ñтилів Ñк головний Ñтиль." #: editor/plugins/theme_editor_plugin.cpp msgid "" "Pin this StyleBox as a main style. Editing its properties will update the " "same properties in all other StyleBoxes of this type." msgstr "" +"Пришпилити цю панель Ñтилів Ñк оÑновний Ñтиль. Ð ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Ð¹Ð¾Ð³Ð¾ влаÑтивоÑтей " +"призведе до Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ñ‚Ð¸Ñ… Ñами влаÑтивоÑтей в уÑÑ–Ñ… інших панелÑÑ… Ñтилів " +"цього типу." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Type" -msgstr "Тип" +msgstr "Додати тип" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Item Type" -msgstr "Додати елемент" +msgstr "Додати тип запиÑу" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Node Types:" -msgstr "Тип вузлів" +msgstr "Типи вузлів:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Show Default" -msgstr "Завантажити типовий" +msgstr "Показати типовий" #: editor/plugins/theme_editor_plugin.cpp msgid "Show default type items alongside items that have been overridden." msgstr "" +"Показати запиÑи Ñтандартних типів разом із запиÑами, Ñкі було перевизначено." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Override All" -msgstr "ПеревизначеннÑ" +msgstr "Перевизначити уÑе" #: editor/plugins/theme_editor_plugin.cpp msgid "Override all default type items." -msgstr "" +msgstr "Перевизначити уÑÑ– запиÑи Ñтандартних типів." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Theme:" -msgstr "Тема" +msgstr "Тема:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Manage Items..." -msgstr "ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ ÑˆÐ°Ð±Ð»Ð¾Ð½Ð°Ð¼Ð¸ екÑпортуваннÑ…" +msgstr "Керувати запиÑами…" #: editor/plugins/theme_editor_plugin.cpp msgid "Add, remove, organize and import Theme items." -msgstr "" +msgstr "Додати, вилучити, упорÑдкувати або імпортувати запиÑи тем." #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Preview" -msgstr "Попередній переглÑд" +msgstr "Додати Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ð¿ÐµÑ€ÐµÐ³Ð»Ñду" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Default Preview" -msgstr "Оновити переглÑд" +msgstr "Типове Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ð¿ÐµÑ€ÐµÐ³Ð»Ñду" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select UI Scene:" -msgstr "Виберіть джерело Ñітки:" +msgstr "Виберіть Ñцену графічного інтерфейÑу:" #: editor/plugins/theme_editor_preview.cpp msgid "" "Toggle the control picker, allowing to visually select control types for " "edit." msgstr "" +"Перемкнути заÑіб вибору керуваннÑ, Ñкий уможливлює візуальний вибір типів " +"ÐºÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð»Ñ Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ." #: editor/plugins/theme_editor_preview.cpp msgid "Toggle Button" @@ -9021,9 +8937,8 @@ msgid "Checked Radio Item" msgstr "Позначений пункт варіанта" #: editor/plugins/theme_editor_preview.cpp -#, fuzzy msgid "Named Separator" -msgstr "Імен. розд." +msgstr "Іменований роздільник" #: editor/plugins/theme_editor_preview.cpp msgid "Submenu" @@ -9076,19 +8991,21 @@ msgstr "Має,Багато,Параметрів" #: editor/plugins/theme_editor_preview.cpp msgid "Invalid path, the PackedScene resource was probably moved or removed." msgstr "" +"Ðекоректний шлÑÑ…. Ймовірно, реÑÑƒÑ€Ñ PackedScene було переÑунуто або вилучено." #: editor/plugins/theme_editor_preview.cpp msgid "Invalid PackedScene resource, must have a Control node at its root." msgstr "" +"Ðекоректний реÑÑƒÑ€Ñ PackedScene. Кореневим вузлом реÑурÑу має бути вузол " +"Control." #: editor/plugins/theme_editor_preview.cpp -#, fuzzy msgid "Invalid file, not a PackedScene resource." -msgstr "ÐеприпуÑтимий файл, це не ÐºÐ¾Ð¼Ð¿Ð¾Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Ð°ÑƒÐ´Ñ–Ð¾-шини." +msgstr "Ðекоректний файл. Файл не Ñ” реÑурÑом PackedScene." #: editor/plugins/theme_editor_preview.cpp msgid "Reload the scene to reflect its most actual state." -msgstr "" +msgstr "Перезавантажити Ñцену Ð´Ð»Ñ Ð²Ñ–Ð´Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ñ—Ñ— найактуальнішого Ñтану." #: editor/plugins/tile_map_editor_plugin.cpp msgid "Erase Selection" @@ -10493,9 +10410,8 @@ msgid "VisualShader" msgstr "VisualShader" #: editor/plugins/visual_shader_editor_plugin.cpp -#, fuzzy msgid "Edit Visual Property:" -msgstr "Змінити візуальну влаÑтивіÑть" +msgstr "Змінити візуальну влаÑтивіÑть:" #: editor/plugins/visual_shader_editor_plugin.cpp msgid "Visual Shader Mode Changed" @@ -10621,9 +10537,8 @@ msgid "Script" msgstr "Скрипт" #: editor/project_export.cpp -#, fuzzy msgid "GDScript Export Mode:" -msgstr "Режим екÑÐ¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñкрипту:" +msgstr "Режим екÑÐ¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ GDScript:" #: editor/project_export.cpp msgid "Text" @@ -10631,21 +10546,20 @@ msgstr "ТекÑÑ‚" #: editor/project_export.cpp msgid "Compiled Bytecode (Faster Loading)" -msgstr "" +msgstr "Зібраний байткод (швидше завантаженнÑ)" #: editor/project_export.cpp msgid "Encrypted (Provide Key Below)" msgstr "Зашифровано (ключ можна вказати нижче)" #: editor/project_export.cpp -#, fuzzy msgid "Invalid Encryption Key (must be 64 hexadecimal characters long)" -msgstr "Ðекоректний ключ ÑˆÐ¸Ñ„Ñ€ÑƒÐ²Ð°Ð½Ð½Ñ (ключ має ÑкладатиÑÑ Ñ–Ð· 64 Ñимволів)" +msgstr "" +"Ðекоректний ключ ÑˆÐ¸Ñ„Ñ€ÑƒÐ²Ð°Ð½Ð½Ñ (ключ має ÑкладатиÑÑ Ñ–Ð· 64 шіÑтнадцÑткових цифр)" #: editor/project_export.cpp -#, fuzzy msgid "GDScript Encryption Key (256-bits as hexadecimal):" -msgstr "Ключ ÑˆÐ¸Ñ„Ñ€ÑƒÐ²Ð°Ð½Ð½Ñ Ñкрипту (256-бітове шіÑтнадцÑткове чиÑло):" +msgstr "Ключ ÑˆÐ¸Ñ„Ñ€ÑƒÐ²Ð°Ð½Ð½Ñ GDScript (256-бітове шіÑтнадцÑткове чиÑло):" #: editor/project_export.cpp msgid "Export PCK/Zip" @@ -10717,7 +10631,6 @@ msgid "Imported Project" msgstr "Імпортований проєкт" #: editor/project_manager.cpp -#, fuzzy msgid "Invalid project name." msgstr "Ðекоректна назва проєкту." @@ -10945,14 +10858,12 @@ msgid "Are you sure to run %d projects at once?" msgstr "Ви Ñправді хочете запуÑтити %d проєктів одночаÑно?" #: editor/project_manager.cpp -#, fuzzy msgid "Remove %d projects from the list?" -msgstr "Вибрати приÑтрій зі ÑпиÑку" +msgstr "Вилучити зі ÑпиÑку %d проєктів?" #: editor/project_manager.cpp -#, fuzzy msgid "Remove this project from the list?" -msgstr "Вибрати приÑтрій зі ÑпиÑку" +msgstr "Вилучити цей проєкт зі ÑпиÑку?" #: editor/project_manager.cpp msgid "" @@ -10985,9 +10896,8 @@ msgid "Project Manager" msgstr "Керівник проєкту" #: editor/project_manager.cpp -#, fuzzy msgid "Local Projects" -msgstr "Проєкти" +msgstr "Локальні проєкти" #: editor/project_manager.cpp msgid "Loading, please wait..." @@ -10998,23 +10908,20 @@ msgid "Last Modified" msgstr "ВоÑтаннє змінено" #: editor/project_manager.cpp -#, fuzzy msgid "Edit Project" -msgstr "ЕкÑпортувати проєкт" +msgstr "Редагувати проєкт" #: editor/project_manager.cpp -#, fuzzy msgid "Run Project" -msgstr "Перейменувати проєкт" +msgstr "ЗапуÑтити проєкт" #: editor/project_manager.cpp msgid "Scan" msgstr "Сканувати" #: editor/project_manager.cpp -#, fuzzy msgid "Scan Projects" -msgstr "Проєкти" +msgstr "Сканувати проєкти" #: editor/project_manager.cpp msgid "Select a Folder to Scan" @@ -11025,14 +10932,12 @@ msgid "New Project" msgstr "Ðовий проєкт" #: editor/project_manager.cpp -#, fuzzy msgid "Import Project" -msgstr "Імпортований проєкт" +msgstr "Імпортувати проєкт" #: editor/project_manager.cpp -#, fuzzy msgid "Remove Project" -msgstr "Перейменувати проєкт" +msgstr "Вилучити проєкт" #: editor/project_manager.cpp msgid "Remove Missing" @@ -11040,12 +10945,11 @@ msgstr "Вилучити пропущене" #: editor/project_manager.cpp msgid "About" -msgstr "Про" +msgstr "ВідомоÑті" #: editor/project_manager.cpp -#, fuzzy msgid "Asset Library Projects" -msgstr "Бібліотека реÑурÑів" +msgstr "Проєкти бібліотеки пакунків" #: editor/project_manager.cpp msgid "Restart Now" @@ -11057,7 +10961,7 @@ msgstr "Вилучити уÑÑ–" #: editor/project_manager.cpp msgid "Also delete project contents (no undo!)" -msgstr "" +msgstr "Також вилучити вміÑÑ‚ проєкту (без можливоÑті ÑкаÑуваннÑ!)" #: editor/project_manager.cpp msgid "Can't run project" @@ -11072,20 +10976,18 @@ msgstr "" "Бажаєте переглÑнути офіційні приклади проєктів з бібліотеки реÑурÑів?" #: editor/project_manager.cpp -#, fuzzy msgid "Filter projects" -msgstr "Фільтрувати влаÑтивоÑті" +msgstr "Фільтр проєктів" #: editor/project_manager.cpp -#, fuzzy msgid "" "This field filters projects by name and last path component.\n" "To filter projects by name and full path, the query must contain at least " "one `/` character." msgstr "" -"Поле пошуку фільтрує проєкти за назвою Ñ– оÑтаннім компонентом шлÑху.\n" +"Це поле фільтрує проєкти за назвою Ñ– оÑтаннім компонентом шлÑху.\n" "Щоб виконати Ñ„Ñ–Ð»ÑŒÑ‚Ñ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¾Ñ”ÐºÑ‚Ñ–Ð² за назвою Ñ– повним шлÑхом, у запиті має " -"бути принаймні один Ñимвол `/`." +"бути принаймні один Ñимвол «/»." #: editor/project_settings_editor.cpp msgid "Key " @@ -11093,7 +10995,7 @@ msgstr "Клавіша " #: editor/project_settings_editor.cpp msgid "Physical Key" -msgstr "" +msgstr "Фізична клавіша" #: editor/project_settings_editor.cpp msgid "Joy Button" @@ -11141,7 +11043,7 @@ msgstr "ПриÑтрій" #: editor/project_settings_editor.cpp msgid " (Physical)" -msgstr "" +msgstr " (фізичний)" #: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp msgid "Press a Key..." @@ -11284,23 +11186,21 @@ msgid "Override for Feature" msgstr "Перевизначено Ð´Ð»Ñ Ð¼Ð¾Ð¶Ð»Ð¸Ð²Ð¾Ñті" #: editor/project_settings_editor.cpp -#, fuzzy msgid "Add %d Translations" -msgstr "Додати переклад" +msgstr "Додати %d перекладів" #: editor/project_settings_editor.cpp msgid "Remove Translation" msgstr "Вилучити переклад" #: editor/project_settings_editor.cpp -#, fuzzy msgid "Translation Resource Remap: Add %d Path(s)" -msgstr "ПереÑпрÑÐ¼ÑƒÐ²Ð°Ð½Ð½Ñ Ñ€ÐµÑурÑу додає переÑпрÑмуваннÑ" +msgstr "Повторна прив'Ñзка реÑурÑів перекладу: Ð´Ð¾Ð´Ð°Ð²Ð°Ð½Ð½Ñ %d шлÑхів" #: editor/project_settings_editor.cpp -#, fuzzy msgid "Translation Resource Remap: Add %d Remap(s)" -msgstr "ПереÑпрÑÐ¼ÑƒÐ²Ð°Ð½Ð½Ñ Ñ€ÐµÑурÑу додає переÑпрÑмуваннÑ" +msgstr "" +"Повторна прив'Ñзка реÑурÑів перекладу: Ð´Ð¾Ð´Ð°Ð²Ð°Ð½Ð½Ñ %d повторних прив'Ñзок" #: editor/project_settings_editor.cpp msgid "Change Resource Remap Language" @@ -11746,12 +11646,15 @@ msgstr "Вилучити вузол «%s»?" msgid "" "Saving the branch as a scene requires having a scene open in the editor." msgstr "" +"Щоб можна було зберегти гілку Ñк Ñцену, Ñцену має бути відкрито у редакторі." #: editor/scene_tree_dock.cpp msgid "" "Saving the branch as a scene requires selecting only one node, but you have " "selected %d nodes." msgstr "" +"Щоб можна було зберегти гілку Ñк Ñцену, має бути позначено лише один вузол, " +"а у Ð²Ð°Ñ Ð¿Ð¾Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¾ %d." #: editor/scene_tree_dock.cpp msgid "" @@ -11760,6 +11663,11 @@ msgid "" "FileSystem dock context menu\n" "or create an inherited scene using Scene > New Inherited Scene... instead." msgstr "" +"Ðе вдалоÑÑ Ð·Ð±ÐµÑ€ÐµÐ³Ñ‚Ð¸ гілку кореневого вузла Ñк екземплÑÑ€ Ñцени.\n" +"Щоб Ñтворити редаговану копію поточної Ñцени, здублюйте Ñ—Ñ— за допомогою " +"контекÑтного меню бічної панелі файлової ÑиÑтеми\n" +"або Ñтворіть уÑпадковану Ñцену за допомогою пункту меню «Сцена > Створити " +"уÑпадковану Ñцену...»." #: editor/scene_tree_dock.cpp msgid "" @@ -11767,6 +11675,10 @@ msgid "" "To create a variation of a scene, you can make an inherited scene based on " "the instanced scene using Scene > New Inherited Scene... instead." msgstr "" +"Ðе вдалоÑÑ Ð·Ð±ÐµÑ€ÐµÐ³Ñ‚Ð¸ гілку вже Ñтвореного екземплÑра Ñцени.\n" +"Щоб Ñтворити варіацію Ñцени, ви можете Ñтворити уÑпадковану Ñцену на оÑнові " +"екземплÑра Ñцени за допомогою пункту меню «Сцена > Створити уÑпадковану " +"Ñцену...»." #: editor/scene_tree_dock.cpp msgid "Save New Scene As..." @@ -12174,6 +12086,8 @@ msgid "" "Warning: Having the script name be the same as a built-in type is usually " "not desired." msgstr "" +"ПопередженнÑ: викориÑÑ‚Ð°Ð½Ð½Ñ Ð´Ð»Ñ Ñкрипту назви, Ñка збігаєтьÑÑ Ñ–Ð· назвою " +"вбудованого типу, зазвичай, Ñ” небажаним." #: editor/script_create_dialog.cpp msgid "Class Name:" @@ -12245,7 +12159,7 @@ msgstr "Помилка копіюваннÑ" #: editor/script_editor_debugger.cpp msgid "Open C++ Source on GitHub" -msgstr "" +msgstr "Відкрити початковий код C++ на GitHub" #: editor/script_editor_debugger.cpp msgid "Video RAM" @@ -12423,6 +12337,16 @@ msgstr "Змінити виÑоту форми циліндра" msgid "Change Ray Shape Length" msgstr "Змінити довжину форми променÑ" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "Задати Ð¿Ð¾Ð»Ð¾Ð¶ÐµÐ½Ð½Ñ Ñ‚Ð¾Ñ‡ÐºÐ¸ кривої" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "Задати Ð¿Ð¾Ð»Ð¾Ð¶ÐµÐ½Ð½Ñ Ñ‚Ð¾Ñ‡ÐºÐ¸ кривої" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "Змінити Ñ€Ð°Ð´Ñ–ÑƒÑ Ñ†Ð¸Ð»Ñ–Ð½Ð´Ñ€Ð°" @@ -12534,14 +12458,12 @@ msgid "Object can't provide a length." msgstr "Об'єкт не може надавати довжину." #: modules/gltf/editor_scene_exporter_gltf_plugin.cpp -#, fuzzy msgid "Export Mesh GLTF2" -msgstr "ЕкÑпортувати бібліотеку Ñіті" +msgstr "ЕкÑпортувати GLTF2 Ñітки" #: modules/gltf/editor_scene_exporter_gltf_plugin.cpp -#, fuzzy msgid "Export GLTF..." -msgstr "ЕкÑпортувати…" +msgstr "ЕкÑпортувати GLTF…" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Next Plane" @@ -12584,9 +12506,8 @@ msgid "GridMap Paint" msgstr "Малюнок GridMap" #: modules/gridmap/grid_map_editor_plugin.cpp -#, fuzzy msgid "GridMap Selection" -msgstr "Вибір Ð·Ð°Ð¿Ð¾Ð²Ð½ÐµÐ½Ð½Ñ GridMap" +msgstr "Вибір GridMap" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Grid Map" @@ -12838,14 +12759,12 @@ msgid "Add Output Port" msgstr "Додати вихідний порт" #: modules/visual_script/visual_script_editor.cpp -#, fuzzy msgid "Change Port Type" -msgstr "Змінити тип" +msgstr "Змірити тип порту" #: modules/visual_script/visual_script_editor.cpp -#, fuzzy msgid "Change Port Name" -msgstr "Змінити назву вхідного порту" +msgstr "Змінити назву порту" #: modules/visual_script/visual_script_editor.cpp msgid "Override an existing built-in function." @@ -12960,9 +12879,8 @@ msgid "Add Preload Node" msgstr "Додати попередньо завантажений вузол" #: modules/visual_script/visual_script_editor.cpp -#, fuzzy msgid "Add Node(s)" -msgstr "Додати вузол" +msgstr "Додати вузли" #: modules/visual_script/visual_script_editor.cpp msgid "Add Node(s) From Tree" @@ -13227,37 +13145,31 @@ msgstr "Вибрати приÑтрій зі ÑпиÑку" #: platform/android/export/export.cpp msgid "Running on %s" -msgstr "" +msgstr "Запущено на %s" #: platform/android/export/export.cpp -#, fuzzy msgid "Exporting APK..." -msgstr "ЕкÑÐ¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÑƒÑього" +msgstr "ЕкÑÐ¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ APK…" #: platform/android/export/export.cpp -#, fuzzy msgid "Uninstalling..." -msgstr "Видалити" +msgstr "ВилученнÑ…" #: platform/android/export/export.cpp -#, fuzzy msgid "Installing to device, please wait..." -msgstr "ЗавантаженнÑ. Будь лаÑка, зачекайте..." +msgstr "Ð’ÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð½Ð° приÑтрій. Будь лаÑка, зачекайте..." #: platform/android/export/export.cpp -#, fuzzy msgid "Could not install to device: %s" -msgstr "Ðе вдалоÑÑ Ð·Ð°Ð¿ÑƒÑтити підпроцеÑ!" +msgstr "Ðе вдалоÑÑ Ð²Ñтановити на приÑтрій: %s" #: platform/android/export/export.cpp -#, fuzzy msgid "Running on device..." -msgstr "ЗапуÑк кориÑтувацького Ñкрипту..." +msgstr "ЗапуÑк на приÑтрої…" #: platform/android/export/export.cpp -#, fuzzy msgid "Could not execute on device." -msgstr "Ðеможливо Ñтворити теку." +msgstr "Ðе вдалоÑÑ Ð²Ð¸ÐºÐ¾Ð½Ð°Ñ‚Ð¸ на приÑтрої." #: platform/android/export/export.cpp msgid "Unable to find the 'apksigner' tool." @@ -13388,40 +13300,38 @@ msgid "" "directory.\n" "The resulting %s is unsigned." msgstr "" +"Ðе вдалоÑÑ Ð·Ð½Ð°Ð¹Ñ‚Ð¸ «apksigner».\n" +"Будь лаÑка, перевірте, чи Ñ” програма доÑтупною у каталозі build-tools набору " +"заÑобів Ð´Ð»Ñ Ñ€Ð¾Ð·Ñ€Ð¾Ð±ÐºÐ¸ Android.\n" +"Отриманий у результаті %s не підпиÑано." #: platform/android/export/export.cpp msgid "Signing debug %s..." -msgstr "" +msgstr "ПідпиÑÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ñ–Ð°Ð³Ð½Ð¾Ñтики %s…" #: platform/android/export/export.cpp -#, fuzzy msgid "Signing release %s..." -msgstr "" -"Ð¡ÐºÐ°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Ñ„Ð°Ð¹Ð»Ñ–Ð²,\n" -"будь лаÑка, зачекайте..." +msgstr "ПідпиÑÑƒÐ²Ð°Ð½Ð½Ñ Ð²Ð¸Ð¿ÑƒÑку %s…" #: platform/android/export/export.cpp -#, fuzzy msgid "Could not find keystore, unable to export." -msgstr "Ðе вдалоÑÑ Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ð¸ шаблон Ð´Ð»Ñ ÐµÐºÑпорту:" +msgstr "Ðе вдалоÑÑ Ð·Ð½Ð°Ð¹Ñ‚Ð¸ Ñховище ключів. Ðеможливо виконати екÑпортуваннÑ." #: platform/android/export/export.cpp msgid "'apksigner' returned with error #%d" -msgstr "" +msgstr "«apksigner» повернуто Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾ помилку із номером %d" #: platform/android/export/export.cpp -#, fuzzy msgid "Verifying %s..." -msgstr "Ð”Ð¾Ð´Ð°Ð²Ð°Ð½Ð½Ñ %s..." +msgstr "ПеревірÑємо %s…" #: platform/android/export/export.cpp msgid "'apksigner' verification of %s failed." -msgstr "" +msgstr "%s не пройдено перевірку за допомогою «apksigner»." #: platform/android/export/export.cpp -#, fuzzy msgid "Exporting for Android" -msgstr "ЕкÑÐ¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÑƒÑього" +msgstr "ЕкÑпорт на Android" #: platform/android/export/export.cpp msgid "Invalid filename! Android App Bundle requires the *.aab extension." @@ -13440,7 +13350,7 @@ msgstr "" #: platform/android/export/export.cpp msgid "Unsupported export format!\n" -msgstr "" +msgstr "Ðепідтримуваний формат екÑпортуваннÑ!\n" #: platform/android/export/export.cpp msgid "" @@ -13468,16 +13378,15 @@ msgstr "" msgid "" "Unable to overwrite res://android/build/res/*.xml files with project name" msgstr "" +"Ðе вдалоÑÑ Ð¿ÐµÑ€ÐµÐ·Ð°Ð¿Ð¸Ñати файли res://android/build/res/*.xml із назвою проєкту" #: platform/android/export/export.cpp -#, fuzzy msgid "Could not export project files to gradle project\n" -msgstr "Ðе вдалоÑÑ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ñ‚Ð¸ project.godot у каталозі проекту." +msgstr "Ðе вдалоÑÑ ÐµÐºÑпортувати файли проєкту до проєкту gradle\n" #: platform/android/export/export.cpp -#, fuzzy msgid "Could not write expansion package file!" -msgstr "Ðе вдалоÑÑ Ð·Ð°Ð¿Ð¸Ñати файл:" +msgstr "Ðе вдалоÑÑ Ð·Ð°Ð¿Ð¸Ñати файл пакунка розширеннÑ!" #: platform/android/export/export.cpp msgid "Building Android Project (gradle)" @@ -13506,21 +13415,20 @@ msgstr "" "дані можна знайти у каталозі проєкту gradle." #: platform/android/export/export.cpp -#, fuzzy msgid "Package not found: %s" -msgstr "Ðе знайдено анімації: «%s»" +msgstr "Пакунок не знайдено: %s" #: platform/android/export/export.cpp -#, fuzzy msgid "Creating APK..." -msgstr "Ð¡Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ ÐºÐ¾Ð½Ñ‚ÑƒÑ€Ñ–Ð²..." +msgstr "Ð¡Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ APK…" #: platform/android/export/export.cpp -#, fuzzy msgid "" "Could not find template APK to export:\n" "%s" -msgstr "Ðе вдалоÑÑ Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ð¸ шаблон Ð´Ð»Ñ ÐµÐºÑпорту:" +msgstr "" +"Ðе вдалоÑÑ Ð·Ð½Ð°Ð¹Ñ‚Ð¸ шаблон APK Ð´Ð»Ñ ÐµÐºÑпортуваннÑ:\n" +"%s" #: platform/android/export/export.cpp msgid "" @@ -13529,16 +13437,17 @@ msgid "" "Please build a template with all required libraries, or uncheck the missing " "architectures in the export preset." msgstr "" +"Ðе виÑтачає бібліотек у шаблоні екÑÐ¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð»Ñ Ð²Ð¸Ð±Ñ€Ð°Ð½Ð¸Ñ… архітектур: %s.\n" +"Будь лаÑка, Ñтворіть шаблон з уÑіма необхідними бібліотеками або зніміть " +"позначку з архітектур із пропущеними бібліотеками у Ñтилі екÑпортуваннÑ." #: platform/android/export/export.cpp -#, fuzzy msgid "Adding files..." -msgstr "Ð”Ð¾Ð´Ð°Ð²Ð°Ð½Ð½Ñ %s..." +msgstr "Ð”Ð¾Ð´Ð°Ð²Ð°Ð½Ð½Ñ Ñ„Ð°Ð¹Ð»Ñ–Ð²â€¦" #: platform/android/export/export.cpp -#, fuzzy msgid "Could not export project files" -msgstr "Ðе вдалоÑÑ Ð·Ð°Ð¿Ð¸Ñати файл:" +msgstr "Ðе вдалоÑÑ ÐµÐºÑпортувати файли проєкту" #: platform/android/export/export.cpp msgid "Aligning APK..." @@ -13546,7 +13455,7 @@ msgstr "Вирівнюємо APK..." #: platform/android/export/export.cpp msgid "Could not unzip temporary unaligned APK." -msgstr "" +msgstr "Ðе вдалоÑÑ Ñ€Ð¾Ð·Ð¿Ð°ÐºÑƒÐ²Ð°Ñ‚Ð¸ тимчаÑовий невирівнÑний APK." #: platform/iphone/export/export.cpp platform/osx/export/export.cpp msgid "Identifier is missing." @@ -13594,45 +13503,40 @@ msgid "Could not write file:" msgstr "Ðе вдалоÑÑ Ð·Ð°Ð¿Ð¸Ñати файл:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Could not read file:" -msgstr "Ðе вдалоÑÑ Ð·Ð°Ð¿Ð¸Ñати файл:" +msgstr "Ðе вдалоÑÑ Ð¿Ñ€Ð¾Ñ‡Ð¸Ñ‚Ð°Ñ‚Ð¸ файл:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Could not read HTML shell:" -msgstr "Ðе вдалоÑÑ Ð¿Ñ€Ð¾Ñ‡Ð¸Ñ‚Ð°Ñ‚Ð¸ Ñпеціальну оболонку HTML:" +msgstr "Ðе вдалоÑÑ Ð¿Ñ€Ð¾Ñ‡Ð¸Ñ‚Ð°Ñ‚Ð¸ оболонку HTML:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Could not create HTTP server directory:" -msgstr "Ðеможливо Ñтворити теку." +msgstr "Ðе вдалоÑÑ Ñтворити каталог на Ñервері HTTP:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Error starting HTTP server:" -msgstr "Помилка Ð·Ð±ÐµÑ€ÐµÐ¶ÐµÐ½Ð½Ñ Ñцени." +msgstr "Помилка під Ñ‡Ð°Ñ Ñпроби запуÑку Ñервера HTTP:" #: platform/osx/export/export.cpp -#, fuzzy msgid "Invalid bundle identifier:" -msgstr "Ðекоректний ідентифікатор:" +msgstr "Ðекоректний ідентифікатор пакунка:" #: platform/osx/export/export.cpp msgid "Notarization: code signing required." -msgstr "" +msgstr "ЗаÑвідченнÑ: потрібен код підпиÑуваннÑ." #: platform/osx/export/export.cpp msgid "Notarization: hardened runtime required." -msgstr "" +msgstr "ЗаÑвідченнÑ: потрібне Ñтійке Ñередовище запуÑку." #: platform/osx/export/export.cpp msgid "Notarization: Apple ID name not specified." -msgstr "" +msgstr "ЗаÑвідченнÑ: не вказано назву ідентифікатора Apple." #: platform/osx/export/export.cpp msgid "Notarization: Apple ID password not specified." -msgstr "" +msgstr "ЗаÑвідченнÑ: не вказано пароль до ідентифікатора Apple." #: platform/uwp/export/export.cpp msgid "Invalid package short name." @@ -14074,6 +13978,9 @@ msgid "" "longer has any effect.\n" "To remove this warning, disable the GIProbe's Compress property." msgstr "" +"ВлаÑтивіÑть GIProbe Compress було визначено заÑтарілою через відомі помилки. " +"Вона більше ні на що не впливає.\n" +"Щоб уÑунути це попередженнÑ, вимкніть влаÑтивіÑть Compress у GIProbe." #: scene/3d/light.cpp msgid "A SpotLight with an angle wider than 90 degrees cannot cast shadows." @@ -14163,14 +14070,20 @@ msgstr "Вузол A Ñ– вузол B має бути різними PhysicsBody" #: scene/3d/portal.cpp msgid "The RoomManager should not be a child or grandchild of a Portal." msgstr "" +"RoomManager не повинен бути дочірнім об'єктом першого або другого Ñ€Ñ–Ð²Ð½Ñ Ð´Ð»Ñ " +"Portal." #: scene/3d/portal.cpp msgid "A Room should not be a child or grandchild of a Portal." msgstr "" +"Ð—Ð°Ð¿Ð¸Ñ Room не повинен бути дочірнім об'єктом першого або другого Ñ€Ñ–Ð²Ð½Ñ Ð´Ð»Ñ " +"Portal." #: scene/3d/portal.cpp msgid "A RoomGroup should not be a child or grandchild of a Portal." msgstr "" +"Ð—Ð°Ð¿Ð¸Ñ RoomGroup не повинен бути дочірнім об'єктом першого або другого Ñ€Ñ–Ð²Ð½Ñ " +"Ð´Ð»Ñ Portal." #: scene/3d/remote_transform.cpp msgid "" @@ -14183,41 +14096,83 @@ msgstr "" #: scene/3d/room.cpp msgid "A Room cannot have another Room as a child or grandchild." msgstr "" +"Ð—Ð°Ð¿Ð¸Ñ Room не може міÑтити дочірнього об'єкта Room першого або другого Ñ€Ñ–Ð²Ð½Ñ " +"вкладеноÑті." #: scene/3d/room.cpp msgid "The RoomManager should not be placed inside a Room." -msgstr "" +msgstr "RoomManager не можна розташовувати у Room." #: scene/3d/room.cpp msgid "A RoomGroup should not be placed inside a Room." -msgstr "" +msgstr "RoomGroup не можна розташовувати у Room." #: scene/3d/room.cpp msgid "" "Room convex hull contains a large number of planes.\n" "Consider simplifying the room bound in order to increase performance." msgstr "" +"Опукла оболонка кімнати міÑтить велику кількіÑть площин.\n" +"Вам варто ÑпроÑтити межу кімнати, щоб підвищити швидкодію." #: scene/3d/room_group.cpp msgid "The RoomManager should not be placed inside a RoomGroup." -msgstr "" +msgstr "RoomManager не можна розташовувати у RoomGroup." #: scene/3d/room_manager.cpp msgid "The RoomList has not been assigned." -msgstr "" +msgstr "RoomList не надано значеннÑ." #: scene/3d/room_manager.cpp msgid "The RoomList node should be a Spatial (or derived from Spatial)." -msgstr "" +msgstr "Вузол RoomList не може бути Spatial (або походити від Spatial)." #: scene/3d/room_manager.cpp msgid "" "Portal Depth Limit is set to Zero.\n" "Only the Room that the Camera is in will render." msgstr "" +"Ð”Ð»Ñ Â«ÐžÐ±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ Ð³Ð»Ð¸Ð±Ð¸Ð½Ð¸ порталу» вÑтановлено нульове значеннÑ.\n" +"Буде оброблено лише Room, у Ñкій перебуває Camera." #: scene/3d/room_manager.cpp msgid "There should only be one RoomManager in the SceneTree." +msgstr "У SceneTree має бути лише один Ð·Ð°Ð¿Ð¸Ñ RoomManager." + +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." msgstr "" #: scene/3d/soft_body.cpp @@ -14283,7 +14238,7 @@ msgstr "Ðе знайдено анімації: «%s»" #: scene/animation/animation_player.cpp msgid "Anim Apply Reset" -msgstr "" +msgstr "Скинути заÑтоÑÑƒÐ²Ð°Ð½Ð½Ñ Ð°Ð½Ñ–Ð¼Ð°Ñ†Ñ–Ñ—" #: scene/animation/animation_tree.cpp msgid "In node '%s', invalid animation: '%s'." @@ -14461,25 +14416,30 @@ msgid "Invalid comparison function for that type." msgstr "Ðекоректна Ñ„ÑƒÐ½ÐºÑ†Ñ–Ñ Ð¿Ð¾Ñ€Ñ–Ð²Ð½ÑÐ½Ð½Ñ Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ типу." #: servers/visual/shader_language.cpp -#, fuzzy msgid "Varying may not be assigned in the '%s' function." -msgstr "Змінні величини можна пов'Ñзувати лише із функцією вузлів." +msgstr "У функції «%s» не може бути надано змінне значеннÑ." #: servers/visual/shader_language.cpp msgid "" "Varyings which assigned in 'vertex' function may not be reassigned in " "'fragment' or 'light'." msgstr "" +"Змінним, Ñким надано Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ñƒ функції «vertex», не можна повторно надавати " +"Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ñƒ «fragment» або «light»." #: servers/visual/shader_language.cpp msgid "" "Varyings which assigned in 'fragment' function may not be reassigned in " "'vertex' or 'light'." msgstr "" +"Змінним, Ñким надано Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ñƒ функції «fragment», не можна повторно " +"надавати Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ñƒ «vertex» або «light»." #: servers/visual/shader_language.cpp msgid "Fragment-stage varying could not been accessed in custom function!" msgstr "" +"ДоÑтуп до змінного Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð½Ð° кроці фрагментації у нетиповій функції " +"неможливий!" #: servers/visual/shader_language.cpp msgid "Assignment to function." @@ -15669,9 +15629,6 @@ msgstr "Сталі не можна змінювати." #~ msgid "I see..." #~ msgstr "Бачу..." -#~ msgid "Can't open '%s'." -#~ msgstr "Ðеможливо відкрити '%s'." - #~ msgid "Ugh" #~ msgstr "Тьху" diff --git a/editor/translations/ur_PK.po b/editor/translations/ur_PK.po index 5c5a6baa8e..fb70bc5703 100644 --- a/editor/translations/ur_PK.po +++ b/editor/translations/ur_PK.po @@ -345,6 +345,7 @@ msgstr "" msgid "Remove Anim Track" msgstr "" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "" @@ -369,10 +370,26 @@ msgstr "" msgid "Anim Insert" msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "سب سکریپشن بنائیں" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "property '%s'" +msgstr "" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "" @@ -939,7 +956,7 @@ msgstr "سب سکریپشن بنائیں" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2261,6 +2278,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -3048,10 +3076,6 @@ msgid "Save & Restart" msgstr "" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "" @@ -3669,6 +3693,15 @@ msgid "Download from:" msgstr ".تمام کا انتخاب" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "سب سکریپشن بنائیں" + +#: editor/export_template_manager.cpp +msgid "Copy Mirror URL" +msgstr "" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8486,6 +8519,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr ".تمام کا انتخاب" @@ -8516,6 +8555,12 @@ msgid "Remove All StyleBox Items" msgstr ".تمام کا انتخاب" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Ù¾Ø³Ù†Ø¯ÛŒØ¯Û Ø§ÙˆÙ¾Ø± منتقل کریں" @@ -12045,6 +12090,16 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr ".تمام کا انتخاب" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr ".تمام کا انتخاب" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -13682,6 +13737,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/vi.po b/editor/translations/vi.po index 531488e640..d50d622215 100644 --- a/editor/translations/vi.po +++ b/editor/translations/vi.po @@ -355,6 +355,7 @@ msgstr "Äổi chế độ vòng lặp hoạt ảnh" msgid "Remove Anim Track" msgstr "Xóa Anim Track" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "Tạo track má»›i cho %s và chèn key?" @@ -379,10 +380,27 @@ msgstr "Tạo" msgid "Anim Insert" msgstr "Chèn Anim" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +msgid "node '%s'" +msgstr "" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "Hoạt ảnh" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "AnimationPlayer không thể tá»± tạo hoạt ảnh, phải nhá» các Player khác." +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "Thuá»™c tÃnh '%s' không tồn tại." + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "Tạo & Chèn Hoạt ảnh" @@ -954,7 +972,7 @@ msgstr "Tạo %s má»›i" msgid "No results for \"%s\"." msgstr "Không tìm thấy kết quả cho \"%s\"." -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2299,6 +2317,17 @@ msgid "New Window" msgstr "Cá»a sổ má»›i" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "Xoay khi cá»a sổ trình chỉnh sá»a được vẽ lại." + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "Tà i nguyên đã nháºp không thể lưu." @@ -3141,10 +3170,6 @@ msgid "Save & Restart" msgstr "Lưu & Khởi động lại" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "Xoay khi cá»a sổ trình chỉnh sá»a được vẽ lại." - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "Cáºp nháºt Liên tục" @@ -3796,6 +3821,16 @@ msgid "Download from:" msgstr "Lá»—i tải" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "Chạy trong Trình duyệt web" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "Sao chép lá»—i" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8623,6 +8658,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "Xóa tất cả các mục" @@ -8653,6 +8694,12 @@ msgid "Remove All StyleBox Items" msgstr "Xóa tất cả các mục" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "Thêm mục Lá»›p" @@ -12282,6 +12329,16 @@ msgstr "Chỉnh chiá»u cao hình trụ" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "Äặt vị trà điểm uốn" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "Äặt vị trà điểm uốn" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "Thay Äổi Bán KÃnh Hình Trụ" @@ -14000,6 +14057,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" diff --git a/editor/translations/zh_CN.po b/editor/translations/zh_CN.po index dbbd935854..8284ac605e 100644 --- a/editor/translations/zh_CN.po +++ b/editor/translations/zh_CN.po @@ -83,7 +83,7 @@ msgid "" msgstr "" "Project-Id-Version: Chinese (Simplified) (Godot Engine)\n" "POT-Creation-Date: 2018-01-20 12:15+0200\n" -"PO-Revision-Date: 2021-08-01 12:02+0000\n" +"PO-Revision-Date: 2021-08-12 14:48+0000\n" "Last-Translator: Haoyu Qiu <timothyqiu32@gmail.com>\n" "Language-Team: Chinese (Simplified) <https://hosted.weblate.org/projects/" "godot-engine/godot/zh_Hans/>\n" @@ -413,6 +413,7 @@ msgstr "更改动画循环模å¼" msgid "Remove Anim Track" msgstr "移除动画轨é“" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "是å¦ä¸º %s 新建轨é“å¹¶æ’入关键帧?" @@ -437,10 +438,28 @@ msgstr "创建" msgid "Anim Insert" msgstr "æ’入动画" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "æ— æ³•æ‰“å¼€ \"%s\"。" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "动画" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "AnimationPlayer ä¸èƒ½åŠ¨ç”»åŒ–è‡ªå·±ï¼Œåªå¯åŠ¨ç”»åŒ–å…¶å®ƒ Player。" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "ä¸å˜åœ¨å±žæ€§â€œ%sâ€ã€‚" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "创建并æ’入动画" @@ -644,9 +663,8 @@ msgid "Go to Previous Step" msgstr "返回上一æ¥" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Apply Reset" -msgstr "é‡ç½®" +msgstr "应用é‡ç½®" #: editor/animation_track_editor.cpp msgid "Optimize Animation" @@ -665,9 +683,8 @@ msgid "Use Bezier Curves" msgstr "使用è´å¡žå°”曲线" #: editor/animation_track_editor.cpp -#, fuzzy msgid "Create RESET Track(s)" -msgstr "粘贴轨é“" +msgstr "创建 RESET 轨é“" #: editor/animation_track_editor.cpp msgid "Anim. Optimizer" @@ -988,7 +1005,6 @@ msgid "Edit..." msgstr "编辑..." #: editor/connections_dialog.cpp -#, fuzzy msgid "Go to Method" msgstr "跳转到方法" @@ -1008,9 +1024,9 @@ msgstr "创建 %s" msgid "No results for \"%s\"." msgstr "未找到 “%sâ€ã€‚" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." -msgstr "" +msgstr "没有针对 %s çš„æè¿°ã€‚" #: editor/create_dialog.cpp editor/editor_file_dialog.cpp #: editor/filesystem_dock.cpp @@ -1110,17 +1126,15 @@ msgid "Owners Of:" msgstr "拥有者:" #: editor/dependency_editor.cpp -#, fuzzy msgid "" "Remove the selected files from the project? (Cannot be undone.)\n" "Depending on your filesystem configuration, the files will either be moved " "to the system trash or deleted permanently." msgstr "" -"是å¦ä»Žé¡¹ç›®ä¸åˆ é™¤æ‰€é€‰æ–‡ä»¶ï¼Ÿï¼ˆæ— æ³•æ’¤é”€ï¼‰\n" -"ä½ å¯ä»¥åœ¨ç³»ç»Ÿå›žæ”¶ç«™ä¸æ¢å¤è¢«åˆ 除的文件。" +"是å¦ä»Žé¡¹ç›®ä¸åˆ é™¤æ‰€é€‰æ–‡ä»¶ï¼Ÿï¼ˆæ— æ³•æ’¤é”€ã€‚ï¼‰\n" +"æ ¹æ®ä½ çš„æ–‡ä»¶ç³»ç»Ÿè®¾ç½®ï¼Œæ–‡ä»¶ä¼šè¢«ç§»åŠ¨è‡³ç³»ç»Ÿå›žæ”¶ç«™æˆ–æ°¸ä¹…åˆ é™¤ã€‚" #: editor/dependency_editor.cpp -#, fuzzy msgid "" "The files being removed are required by other resources in order for them to " "work.\n" @@ -1128,9 +1142,9 @@ msgid "" "Depending on your filesystem configuration, the files will either be moved " "to the system trash or deleted permanently." msgstr "" -"è¦åˆ é™¤çš„æ–‡ä»¶è¢«å…¶ä»–èµ„æºæ‰€ä¾èµ–。\n" -"ä»ç„¶è¦åˆ 除å—ï¼Ÿï¼ˆæ— æ³•æ’¤é”€ï¼‰\n" -"ä½ å¯ä»¥åœ¨ç³»ç»Ÿå›žæ”¶ç«™ä¸æ¢å¤è¢«åˆ 除的文件。" +"其它资æºéœ€è¦è¿™äº›å³å°†è¢«åˆ 除的文件æ‰èƒ½æ£å¸¸å·¥ä½œã€‚\n" +"ä»ç„¶è¦åˆ 除å—ï¼Ÿï¼ˆæ— æ³•æ’¤é”€ã€‚ï¼‰\n" +"æ ¹æ®ä½ çš„æ–‡ä»¶ç³»ç»Ÿè®¾ç½®ï¼Œæ–‡ä»¶ä¼šè¢«ç§»åŠ¨è‡³ç³»ç»Ÿå›žæ”¶ç«™æˆ–æ°¸ä¹…åˆ é™¤ã€‚" #: editor/dependency_editor.cpp msgid "Cannot remove:" @@ -1298,41 +1312,36 @@ msgid "Licenses" msgstr "许å¯è¯" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Error opening asset file for \"%s\" (not in ZIP format)." -msgstr "æ‰“å¼€åŒ…æ–‡ä»¶æ—¶å‡ºé”™ï¼ˆéž ZIP æ ¼å¼ï¼‰ã€‚" +msgstr "打开“%sâ€çš„ç´ ææ–‡ä»¶æ—¶å‡ºé”™ï¼ˆéž ZIP æ ¼å¼ï¼‰ã€‚" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "%s (already exists)" msgstr "%s(已å˜åœ¨ï¼‰" #: editor/editor_asset_installer.cpp msgid "Contents of asset \"%s\" - %d file(s) conflict with your project:" -msgstr "" +msgstr "ç´ æâ€œ%sâ€çš„内容 - %d ä¸ªæ–‡ä»¶ä¸Žä½ çš„é¡¹ç›®å†²çªï¼š" #: editor/editor_asset_installer.cpp msgid "Contents of asset \"%s\" - No files conflict with your project:" -msgstr "" +msgstr "ç´ æâ€œ%sâ€çš„内容 - æ²¡æœ‰æ–‡ä»¶ä¸Žä½ çš„é¡¹ç›®å†²çªï¼š" #: editor/editor_asset_installer.cpp msgid "Uncompressing Assets" msgstr "æ£åœ¨è§£åŽ‹ç´ æ" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "The following files failed extraction from asset \"%s\":" -msgstr "ä»¥ä¸‹æ–‡ä»¶æ— æ³•ä»ŽåŒ…ä¸æå–:" +msgstr "ä»¥ä¸‹æ–‡ä»¶æ— æ³•ä»Žç´ æâ€œ%sâ€ä¸æå–:" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "(and %s more files)" -msgstr "以åŠå…¶å®ƒ %s 个文件。" +msgstr "(以åŠå…¶å®ƒ %s 个文件)" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Asset \"%s\" installed successfully!" -msgstr "软件包安装æˆåŠŸï¼" +msgstr "ç´ æâ€œ%sâ€å®‰è£…æˆåŠŸï¼" #: editor/editor_asset_installer.cpp #: editor/plugins/asset_library_editor_plugin.cpp @@ -1344,9 +1353,8 @@ msgid "Install" msgstr "安装" #: editor/editor_asset_installer.cpp -#, fuzzy msgid "Asset Installer" -msgstr "程åºåŒ…安装程åº" +msgstr "ç´ æå®‰è£…器" #: editor/editor_audio_buses.cpp msgid "Speakers" @@ -1409,7 +1417,6 @@ msgid "Bypass" msgstr "æ—通" #: editor/editor_audio_buses.cpp -#, fuzzy msgid "Bus Options" msgstr "总线选项" @@ -1577,13 +1584,12 @@ msgid "Can't add autoload:" msgstr "æ— æ³•åŠ è½½ Autoload:" #: editor/editor_autoload_settings.cpp -#, fuzzy msgid "%s is an invalid path. File does not exist." -msgstr "文件ä¸å˜åœ¨ã€‚" +msgstr "%s æ˜¯æ— æ•ˆè·¯å¾„ã€‚æ–‡ä»¶ä¸å˜åœ¨ã€‚" #: editor/editor_autoload_settings.cpp msgid "%s is an invalid path. Not in resource path (res://)." -msgstr "" +msgstr "%s æ˜¯æ— æ•ˆè·¯å¾„ã€‚ä¸æ˜¯èµ„æºè·¯å¾„(res://)。" #: editor/editor_autoload_settings.cpp msgid "Add AutoLoad" @@ -1607,9 +1613,8 @@ msgid "Name" msgstr "åç§°" #: editor/editor_autoload_settings.cpp -#, fuzzy msgid "Global Variable" -msgstr "å˜é‡" +msgstr "全局å˜é‡" #: editor/editor_data.cpp msgid "Paste Params" @@ -1775,48 +1780,47 @@ msgstr "坼入颿¿" #: editor/editor_feature_profile.cpp msgid "Allows to view and edit 3D scenes." -msgstr "" +msgstr "å…许查看并编辑 3D 场景。" #: editor/editor_feature_profile.cpp msgid "Allows to edit scripts using the integrated script editor." -msgstr "" +msgstr "å…许使用内置脚本编辑器编辑脚本。" #: editor/editor_feature_profile.cpp msgid "Provides built-in access to the Asset Library." -msgstr "" +msgstr "æä¾›å¯¹ç´ æåº“的内置访问。" #: editor/editor_feature_profile.cpp msgid "Allows editing the node hierarchy in the Scene dock." -msgstr "" +msgstr "å…è®¸åœ¨åœºæ™¯é¢æ¿ä¸ç¼–辑节点层级。" #: editor/editor_feature_profile.cpp msgid "" "Allows to work with signals and groups of the node selected in the Scene " "dock." -msgstr "" +msgstr "å…è®¸åœ¨åœºæ™¯é¢æ¿æ“作所选节点的信å·å’Œåˆ†ç»„。" #: editor/editor_feature_profile.cpp msgid "Allows to browse the local file system via a dedicated dock." -msgstr "" +msgstr "å…è®¸ä½¿ç”¨ä¸“é—¨çš„é¢æ¿æµè§ˆæœ¬åœ°æ–‡ä»¶ç³»ç»Ÿã€‚" #: editor/editor_feature_profile.cpp msgid "" "Allows to configure import settings for individual assets. Requires the " "FileSystem dock to function." -msgstr "" +msgstr "å…许为å„ä¸ªç´ æé…ç½®å¯¼å…¥è®¾ç½®ã€‚æ–‡ä»¶ç³»ç»Ÿé¢æ¿éœ€è¦å¯ç”¨ã€‚" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "(current)" msgstr "(当å‰ï¼‰" #: editor/editor_feature_profile.cpp msgid "(none)" -msgstr "" +msgstr "ï¼ˆæ— ï¼‰" #: editor/editor_feature_profile.cpp msgid "Remove currently selected profile, '%s'? Cannot be undone." -msgstr "" +msgstr "è¦åˆ 除当剿‰€é€‰çš„é…置文件“%sâ€å—ï¼Ÿæ— æ³•æ’¤é”€ã€‚" #: editor/editor_feature_profile.cpp msgid "Profile must be a valid filename and must not contain '.'" @@ -1847,19 +1851,16 @@ msgid "Enable Contextual Editor" msgstr "å¯ç”¨ä¸Šä¸‹æ–‡ç¼–辑器" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Class Properties:" -msgstr "属性:" +msgstr "类属性:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Main Features:" -msgstr "特性" +msgstr "主è¦ç‰¹æ€§ï¼š" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Nodes and Classes:" -msgstr "å¯ç”¨çš„类:" +msgstr "节点和类:" #: editor/editor_feature_profile.cpp msgid "File '%s' format is invalid, import aborted." @@ -1876,7 +1877,6 @@ msgid "Error saving profile to path: '%s'." msgstr "å°†é…置文件ä¿å˜åˆ°è·¯å¾„ “%s†时出错。" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Reset to Default" msgstr "é‡ç½®ä¸ºé»˜è®¤å€¼" @@ -1885,14 +1885,12 @@ msgid "Current Profile:" msgstr "当å‰é…置文件:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Create Profile" -msgstr "åˆ é™¤é…置文件" +msgstr "创建é…置文件" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Remove Profile" -msgstr "移除图å—" +msgstr "åˆ é™¤é…置文件" #: editor/editor_feature_profile.cpp msgid "Available Profiles:" @@ -1912,18 +1910,16 @@ msgid "Export" msgstr "导出" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Configure Selected Profile:" -msgstr "当å‰é…置文件:" +msgstr "é…置所选é…置文件:" #: editor/editor_feature_profile.cpp -#, fuzzy msgid "Extra Options:" -msgstr "纹ç†é€‰é¡¹" +msgstr "更多选项:" #: editor/editor_feature_profile.cpp msgid "Create or import a profile to edit available classes and properties." -msgstr "" +msgstr "创建或导入é…置文件以编辑å¯ç”¨çš„类和属性。" #: editor/editor_feature_profile.cpp msgid "New profile name:" @@ -1950,7 +1946,6 @@ msgid "Select Current Folder" msgstr "é€‰æ‹©å½“å‰æ–‡ä»¶å¤¹" #: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp -#, fuzzy msgid "File exists, overwrite?" msgstr "文件已å˜åœ¨ï¼Œæ˜¯å¦è¦†ç›–?" @@ -2343,6 +2338,17 @@ msgid "New Window" msgstr "新窗å£" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "编辑器窗å£é‡ç»˜æ—¶æ—‹è½¬ã€‚" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "å¯¼å…¥çš„èµ„æºæ— 法ä¿å˜ã€‚" @@ -2503,9 +2509,9 @@ msgid "" "Please read the documentation relevant to importing scenes to better " "understand this workflow." msgstr "" -"场景已被导入,所åšçš„æ›´æ”¹å°†ä¸ä¼šä¿ç•™ã€‚\n" -"请实例化或继承该场景以å…许对其进行更改。\n" -"请阅读与导入场景相关的文档,以更佳ç†è§£æ¤å·¥ä½œæµã€‚" +"该场景是被导入的场景,ä¸ä¼šä¿ç•™å¯¹å®ƒçš„æ›´æ”¹ã€‚\n" +"实例化或继承该场景就å¯ä»¥å¯¹å…¶è¿›è¡Œæ›´æ”¹ã€‚\n" +"请阅读与导入场景相关的文档,以便更好地ç†è§£æ¤å·¥ä½œæµã€‚" #: editor/editor_node.cpp msgid "" @@ -2560,14 +2566,13 @@ msgstr "是å¦åœ¨å…³é—å‰ä¿å˜å¯¹ “%s†的更改?" msgid "" "The current scene has no root node, but %d modified external resource(s) " "were saved anyway." -msgstr "" +msgstr "当å‰åœºæ™¯æ²¡æœ‰æ ¹èŠ‚ç‚¹ï¼Œä¸è¿‡ä¿å˜äº† %d 个已修改的外部资æºã€‚" #: editor/editor_node.cpp -#, fuzzy msgid "" "A root node is required to save the scene. You can add a root node using the " "Scene tree dock." -msgstr "å¿…é¡»æœ‰æ ¹èŠ‚ç‚¹æ‰å¯ä¿å˜åœºæ™¯ã€‚" +msgstr "å¿…é¡»æœ‰æ ¹èŠ‚ç‚¹æ‰èƒ½ä¿å˜åœºæ™¯ã€‚ä½ å¯ä»¥é€šè¿‡åœºæ™¯é¢æ¿æ·»åŠ æ ¹èŠ‚ç‚¹ã€‚" #: editor/editor_node.cpp msgid "Save Scene As..." @@ -2938,9 +2943,8 @@ msgid "Orphan Resource Explorer..." msgstr "å¤ç«‹èµ„æºæµè§ˆå™¨..." #: editor/editor_node.cpp -#, fuzzy msgid "Reload Current Project" -msgstr "é‡å‘½å项目" +msgstr "釿–°åŠ è½½å½“å‰é¡¹ç›®" #: editor/editor_node.cpp msgid "Quit to Project List" @@ -3088,22 +3092,20 @@ msgid "Help" msgstr "帮助" #: editor/editor_node.cpp -#, fuzzy msgid "Online Documentation" -msgstr "打开文档" +msgstr "在线文档" #: editor/editor_node.cpp msgid "Questions & Answers" -msgstr "" +msgstr "问与ç”" #: editor/editor_node.cpp msgid "Report a Bug" msgstr "报告问题" #: editor/editor_node.cpp -#, fuzzy msgid "Suggest a Feature" -msgstr "设置值" +msgstr "æäº¤æ–°ç‰¹æ€§å»ºè®®" #: editor/editor_node.cpp msgid "Send Docs Feedback" @@ -3114,9 +3116,8 @@ msgid "Community" msgstr "社区" #: editor/editor_node.cpp -#, fuzzy msgid "About Godot" -msgstr "关于" +msgstr "关于 Godot" #: editor/editor_node.cpp msgid "Support Godot Development" @@ -3168,10 +3169,6 @@ msgid "Save & Restart" msgstr "ä¿å˜å¹¶é‡å¯" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "编辑器窗å£é‡ç»˜æ—¶æ—‹è½¬ã€‚" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "æŒç»æ›´æ–°" @@ -3212,14 +3209,12 @@ msgid "Manage Templates" msgstr "ç®¡ç†æ¨¡æ¿" #: editor/editor_node.cpp -#, fuzzy msgid "Install from file" msgstr "从文件安装" #: editor/editor_node.cpp -#, fuzzy msgid "Select android sources file" -msgstr "选择æºç½‘æ ¼ï¼š" +msgstr "选择 Android æºæ–‡ä»¶" #: editor/editor_node.cpp msgid "" @@ -3299,9 +3294,8 @@ msgid "Select" msgstr "选择" #: editor/editor_node.cpp -#, fuzzy msgid "Select Current" -msgstr "é€‰æ‹©å½“å‰æ–‡ä»¶å¤¹" +msgstr "选择当å‰" #: editor/editor_node.cpp msgid "Open 2D Editor" @@ -3336,9 +3330,8 @@ msgid "No sub-resources found." msgstr "找ä¸åˆ°å资æºã€‚" #: editor/editor_path.cpp -#, fuzzy msgid "Open a list of sub-resources." -msgstr "找ä¸åˆ°å资æºã€‚" +msgstr "打开å资æºåˆ—表。" #: editor/editor_plugin.cpp msgid "Creating Mesh Previews" @@ -3365,12 +3358,10 @@ msgid "Update" msgstr "æ›´æ–°" #: editor/editor_plugin_settings.cpp -#, fuzzy msgid "Version" -msgstr "版本:" +msgstr "版本" #: editor/editor_plugin_settings.cpp -#, fuzzy msgid "Author" msgstr "作者" @@ -3385,14 +3376,12 @@ msgid "Measure:" msgstr "测é‡ï¼š" #: editor/editor_profiler.cpp -#, fuzzy msgid "Frame Time (ms)" -msgstr "帧时间(秒)" +msgstr "帧时间(毫秒)" #: editor/editor_profiler.cpp -#, fuzzy msgid "Average Time (ms)" -msgstr "平凿—¶é—´ï¼ˆç§’)" +msgstr "平凿—¶é—´ï¼ˆæ¯«ç§’)" #: editor/editor_profiler.cpp msgid "Frame %" @@ -3419,6 +3408,11 @@ msgid "" "functions called by that function.\n" "Use this to find individual functions to optimize." msgstr "" +"全部:包括该函数调用的其它函数的时间。\n" +"使用该选项寻找瓶颈。\n" +"\n" +"仅自己:åªè®¡ç®—消耗在该函数本身的时间,ä¸åŒ…å«è¯¥å‡½æ•°æ‰€è°ƒç”¨çš„其它函数。\n" +"使用该选项寻找需è¦ä¼˜åŒ–的函数。" #: editor/editor_profiler.cpp msgid "Frame #:" @@ -3536,7 +3530,6 @@ msgid "Paste" msgstr "粘贴" #: editor/editor_resource_picker.cpp editor/property_editor.cpp -#, fuzzy msgid "Convert to %s" msgstr "转æ¢ä¸º %s" @@ -3586,9 +3579,8 @@ msgid "Did you forget the '_run' method?" msgstr "是å¦é—æ¼äº† _run() 方法?" #: editor/editor_spin_slider.cpp -#, fuzzy msgid "Hold %s to round to integers. Hold Shift for more precise changes." -msgstr "æŒ‰ä½ Ctrl é”®æ¥å–整。 æŒ‰ä½ Shift é”®èŽ·å–æ›´ç²¾ç¡®çš„å˜åŒ–。" +msgstr "æŒ‰ä½ %s å–æ•´ã€‚ æŒ‰ä½ Shift èŽ·å–æ›´ç²¾ç¡®çš„å˜åŒ–。" #: editor/editor_sub_scene.cpp msgid "Select Node(s) to Import" @@ -3608,49 +3600,43 @@ msgstr "从节点ä¸å¯¼å…¥ï¼š" #: editor/export_template_manager.cpp msgid "Open the folder containing these templates." -msgstr "" +msgstr "打开包å«è¿™äº›æ¨¡æ¿çš„æ–‡ä»¶å¤¹ã€‚" #: editor/export_template_manager.cpp msgid "Uninstall these templates." -msgstr "" +msgstr "å¸è½½è¿™äº›æ¨¡æ¿ã€‚" #: editor/export_template_manager.cpp -#, fuzzy msgid "There are no mirrors available." -msgstr "文件 “%s†ä¸å˜åœ¨ã€‚" +msgstr "没有å¯ç”¨çš„镜åƒã€‚" #: editor/export_template_manager.cpp -#, fuzzy msgid "Retrieving the mirror list..." -msgstr "检索镜åƒï¼Œè¯·ç‰å¾…..." +msgstr "æ£åœ¨èŽ·å–镜åƒåˆ—表……" #: editor/export_template_manager.cpp msgid "Starting the download..." -msgstr "" +msgstr "æ£åœ¨å¼€å§‹ä¸‹è½½â€¦â€¦" #: editor/export_template_manager.cpp msgid "Error requesting URL:" msgstr "请求 URL 时出错:" #: editor/export_template_manager.cpp -#, fuzzy msgid "Connecting to the mirror..." -msgstr "æ£åœ¨è¿žæŽ¥é•œåƒç½‘ç«™..." +msgstr "æ£åœ¨è¿žæŽ¥é•œåƒâ€¦â€¦" #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't resolve the requested address." -msgstr "æ— æ³•è§£æžä¸»æœºå:" +msgstr "æ— æ³•è§£æžè¯·æ±‚地å€ã€‚" #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't connect to the mirror." -msgstr "æ— æ³•è¿žæŽ¥åˆ°ä¸»æœºï¼š" +msgstr "æ— æ³•è¿žæŽ¥åˆ°é•œåƒã€‚" #: editor/export_template_manager.cpp -#, fuzzy msgid "No response from the mirror." -msgstr "ä¸»æœºæ— å“应:" +msgstr "é•œåƒæ— å“应。" #: editor/export_template_manager.cpp #: editor/plugins/asset_library_editor_plugin.cpp @@ -3658,18 +3644,16 @@ msgid "Request failed." msgstr "请求失败。" #: editor/export_template_manager.cpp -#, fuzzy msgid "Request ended up in a redirect loop." -msgstr "请求失败,é‡å®šå‘次数过多" +msgstr "请求进入了é‡å®šå‘循环。" #: editor/export_template_manager.cpp -#, fuzzy msgid "Request failed:" -msgstr "请求失败。" +msgstr "请求失败:" #: editor/export_template_manager.cpp msgid "Download complete; extracting templates..." -msgstr "" +msgstr "下载完æˆï¼›æ£åœ¨è§£åŽ‹æ¨¡æ¿â€¦â€¦" #: editor/export_template_manager.cpp msgid "Cannot remove temporary file:" @@ -3688,13 +3672,12 @@ msgid "Error getting the list of mirrors." msgstr "获å–镜åƒåˆ—表时出错。" #: editor/export_template_manager.cpp -#, fuzzy msgid "Error parsing JSON with the list of mirrors. Please report this issue!" msgstr "è§£æžé•œåƒåˆ—表 JSON 时出错。请æäº¤æ¤é—®é¢˜ï¼" #: editor/export_template_manager.cpp msgid "Best available mirror" -msgstr "" +msgstr "最佳å¯ç”¨é•œåƒ" #: editor/export_template_manager.cpp msgid "" @@ -3745,24 +3728,20 @@ msgid "SSL Handshake Error" msgstr "SSL æ¡æ‰‹é”™è¯¯" #: editor/export_template_manager.cpp -#, fuzzy msgid "Can't open the export templates file." -msgstr "æ— æ³•æ‰“å¼€ ZIP 导出模æ¿ã€‚" +msgstr "æ— æ³•æ‰“å¼€å¯¼å‡ºæ¨¡æ¿æ–‡ä»¶ã€‚" #: editor/export_template_manager.cpp -#, fuzzy msgid "Invalid version.txt format inside the export templates file: %s." -msgstr "模æ¿ä¸çš„ version.txt æ ¼å¼æ— 效:%s。" +msgstr "导出模æ¿ä¸çš„ version.txt æ ¼å¼æ— 效:%s。" #: editor/export_template_manager.cpp -#, fuzzy msgid "No version.txt found inside the export templates file." -msgstr "模æ¿ä¸æ²¡æœ‰æ‰¾åˆ° version.txt。" +msgstr "导出模æ¿ä¸æ²¡æœ‰ version.txt。" #: editor/export_template_manager.cpp -#, fuzzy msgid "Error creating path for extracting templates:" -msgstr "创建模æ¿è·¯å¾„出错:" +msgstr "为解压模æ¿åˆ›å»ºè·¯å¾„时出错:" #: editor/export_template_manager.cpp msgid "Extracting Export Templates" @@ -3773,9 +3752,8 @@ msgid "Importing:" msgstr "æ£åœ¨å¯¼å…¥ï¼š" #: editor/export_template_manager.cpp -#, fuzzy msgid "Remove templates for the version '%s'?" -msgstr "是å¦ç§»é™¤æ¨¡æ¿ç‰ˆæœ¬ “%sâ€ï¼Ÿ" +msgstr "是å¦ç§»é™¤æ¨¡æ¿ç‰ˆæœ¬â€œ%sâ€ï¼Ÿ" #: editor/export_template_manager.cpp msgid "Uncompressing Android Build Sources" @@ -3791,58 +3769,63 @@ msgstr "当å‰ç‰ˆæœ¬ï¼š" #: editor/export_template_manager.cpp msgid "Export templates are missing. Download them or install from a file." -msgstr "" +msgstr "缺失导出模æ¿ã€‚请下载或从文件安装。" #: editor/export_template_manager.cpp msgid "Export templates are installed and ready to be used." -msgstr "" +msgstr "导出模æ¿å·²å®‰è£…就绪。" #: editor/export_template_manager.cpp -#, fuzzy msgid "Open Folder" -msgstr "打开文件" +msgstr "打开文件夹" #: editor/export_template_manager.cpp msgid "Open the folder containing installed templates for the current version." -msgstr "" +msgstr "打开包å«å½“å‰ç‰ˆæœ¬å·²å®‰è£…模æ¿çš„æ–‡ä»¶å¤¹ã€‚" #: editor/export_template_manager.cpp msgid "Uninstall" msgstr "å¸è½½" #: editor/export_template_manager.cpp -#, fuzzy msgid "Uninstall templates for the current version." -msgstr "计数器åˆå§‹å€¼" +msgstr "å¸è½½å½“å‰ç‰ˆæœ¬çš„æ¨¡æ¿ã€‚" #: editor/export_template_manager.cpp -#, fuzzy msgid "Download from:" -msgstr "下载错误" +msgstr "下载:" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "在æµè§ˆå™¨ä¸è¿è¡Œ" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "å¤åˆ¶é”™è¯¯ä¿¡æ¯" #: editor/export_template_manager.cpp msgid "Download and Install" -msgstr "" +msgstr "下载并安装" #: editor/export_template_manager.cpp msgid "" "Download and install templates for the current version from the best " "possible mirror." -msgstr "" +msgstr "从最佳的镜åƒä¸‹è½½å½“å‰ç‰ˆæœ¬çš„æ¨¡æ¿å¹¶å®‰è£…。" #: editor/export_template_manager.cpp msgid "Official export templates aren't available for development builds." msgstr "开呿ž„建下官方导出模æ¿ä¸å¯ç”¨ã€‚" #: editor/export_template_manager.cpp -#, fuzzy msgid "Install from File" msgstr "从文件安装" #: editor/export_template_manager.cpp -#, fuzzy msgid "Install templates from a local file." -msgstr "从 ZIP 文件ä¸å¯¼å…¥æ¨¡æ¿" +msgstr "从本地文件安装模æ¿ã€‚" #: editor/export_template_manager.cpp editor/find_in_files.cpp #: editor/progress_dialog.cpp scene/gui/dialogs.cpp @@ -3850,19 +3833,16 @@ msgid "Cancel" msgstr "å–æ¶ˆ" #: editor/export_template_manager.cpp -#, fuzzy msgid "Cancel the download of the templates." -msgstr "æ— æ³•æ‰“å¼€ ZIP 导出模æ¿ã€‚" +msgstr "å–æ¶ˆä¸‹è½½æ¨¡æ¿ã€‚" #: editor/export_template_manager.cpp -#, fuzzy msgid "Other Installed Versions:" -msgstr "已安装版本:" +msgstr "其它已安装版本:" #: editor/export_template_manager.cpp -#, fuzzy msgid "Uninstall Template" -msgstr "å¸è½½" +msgstr "å¸è½½æ¨¡æ¿" #: editor/export_template_manager.cpp msgid "Select Template File" @@ -3877,6 +3857,8 @@ msgid "" "The templates will continue to download.\n" "You may experience a short editor freeze when they finish." msgstr "" +"模æ¿ä¸‹è½½ä»ä¼šç»§ç»ã€‚\n" +"å®Œæˆæ—¶ä½ å¯èƒ½ä¼šæ„Ÿå—åˆ°ç¼–è¾‘å™¨çš„çŸæš‚冻结。" #: editor/filesystem_dock.cpp msgid "Favorites" @@ -3972,7 +3954,7 @@ msgstr "打开场景" #: editor/filesystem_dock.cpp msgid "Instance" -msgstr "实例" +msgstr "实例化" #: editor/filesystem_dock.cpp msgid "Add to Favorites" @@ -4019,35 +4001,32 @@ msgid "Collapse All" msgstr "全部折å " #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort files" -msgstr "æœç´¢æ–‡ä»¶" +msgstr "文件排åº" #: editor/filesystem_dock.cpp msgid "Sort by Name (Ascending)" -msgstr "" +msgstr "按å称(å‡åºï¼‰" #: editor/filesystem_dock.cpp msgid "Sort by Name (Descending)" -msgstr "" +msgstr "按å称(é™åºï¼‰" #: editor/filesystem_dock.cpp msgid "Sort by Type (Ascending)" -msgstr "" +msgstr "按类型(å‡åºï¼‰" #: editor/filesystem_dock.cpp msgid "Sort by Type (Descending)" -msgstr "" +msgstr "按类型(é™åºï¼‰" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort by Last Modified" -msgstr "修改时间" +msgstr "按最近修改" #: editor/filesystem_dock.cpp -#, fuzzy msgid "Sort by First Modified" -msgstr "修改时间" +msgstr "按最早修改" #: editor/filesystem_dock.cpp msgid "Duplicate..." @@ -4059,7 +4038,7 @@ msgstr "é‡å‘½å为..." #: editor/filesystem_dock.cpp msgid "Focus the search box" -msgstr "" +msgstr "èšç„¦æœç´¢æ¡†" #: editor/filesystem_dock.cpp msgid "Previous Folder/File" @@ -4363,14 +4342,12 @@ msgid "Failed to load resource." msgstr "åŠ è½½èµ„æºå¤±è´¥ã€‚" #: editor/inspector_dock.cpp -#, fuzzy msgid "Copy Properties" -msgstr "属性" +msgstr "å¤åˆ¶å±žæ€§" #: editor/inspector_dock.cpp -#, fuzzy msgid "Paste Properties" -msgstr "属性" +msgstr "粘贴属性" #: editor/inspector_dock.cpp msgid "Make Sub-Resources Unique" @@ -4395,23 +4372,20 @@ msgid "Save As..." msgstr "å¦å˜ä¸º..." #: editor/inspector_dock.cpp -#, fuzzy msgid "Extra resource options." -msgstr "ä¸åœ¨èµ„æºè·¯å¾„下。" +msgstr "更多资æºé€‰é¡¹ã€‚" #: editor/inspector_dock.cpp -#, fuzzy msgid "Edit Resource from Clipboard" -msgstr "编辑资æºå‰ªè´´æ¿" +msgstr "编辑剪贴æ¿ä¸çš„资æº" #: editor/inspector_dock.cpp msgid "Copy Resource" msgstr "å¤åˆ¶èµ„æº" #: editor/inspector_dock.cpp -#, fuzzy msgid "Make Resource Built-In" -msgstr "转为内置" +msgstr "将资æºè½¬ä¸ºå†…ç½®" #: editor/inspector_dock.cpp msgid "Go to the previous edited object in history." @@ -4426,9 +4400,8 @@ msgid "History of recently edited objects." msgstr "最近编辑历å²å¯¹è±¡ã€‚" #: editor/inspector_dock.cpp -#, fuzzy msgid "Open documentation for this object." -msgstr "打开文档" +msgstr "打开该对象的文档。" #: editor/inspector_dock.cpp editor/scene_tree_dock.cpp msgid "Open Documentation" @@ -4439,9 +4412,8 @@ msgid "Filter properties" msgstr "ç›é€‰å±žæ€§" #: editor/inspector_dock.cpp -#, fuzzy msgid "Manage object properties." -msgstr "对象属性。" +msgstr "管ç†å¯¹è±¡å±žæ€§ã€‚" #: editor/inspector_dock.cpp msgid "Changes may be lost!" @@ -4684,9 +4656,8 @@ msgid "Blend:" msgstr "æ··åˆï¼š" #: editor/plugins/animation_blend_tree_editor_plugin.cpp -#, fuzzy msgid "Parameter Changed:" -msgstr "傿•°å·²æ›´æ”¹" +msgstr "ä¿®æ”¹å‚æ•°ï¼š" #: editor/plugins/animation_blend_tree_editor_plugin.cpp #: editor/plugins/animation_tree_player_editor_plugin.cpp @@ -5408,11 +5379,11 @@ msgstr "全部" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Search templates, projects, and demos" -msgstr "" +msgstr "æœç´¢æ¨¡æ¿ã€é¡¹ç›®ã€æ¼”示" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Search assets (excluding templates, projects, and demos)" -msgstr "" +msgstr "æœç´¢ç´ æï¼ˆä¸åŒ…嫿¨¡æ¿ã€é¡¹ç›®ã€æ¼”示)" #: editor/plugins/asset_library_editor_plugin.cpp msgid "Import..." @@ -5456,7 +5427,7 @@ msgstr "ç´ æ ZIP 文件" #: editor/plugins/audio_stream_editor_plugin.cpp msgid "Audio Preview Play/Pause" -msgstr "" +msgstr "éŸ³é¢‘é¢„è§ˆæ’æ”¾/æš‚åœ" #: editor/plugins/baked_lightmap_editor_plugin.cpp msgid "" @@ -5705,13 +5676,12 @@ msgstr "编辑锚点" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "" "Project Camera Override\n" "Overrides the running project's camera with the editor viewport camera." msgstr "" -"游æˆç›¸æœºè¦†ç›–\n" -"使用编辑器视图相机覆盖游æˆç›¸æœºã€‚" +"项目相机覆盖\n" +"使用编辑器视图相机覆盖æ£åœ¨è¿è¡Œçš„项目ä¸çš„相机。" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp @@ -5720,6 +5690,8 @@ msgid "" "No project instance running. Run the project from the editor to use this " "feature." msgstr "" +"项目相机覆盖\n" +"没有æ£åœ¨è¿è¡Œçš„项目实例。请先在编辑器ä¸è¿è¡Œé¡¹ç›®å†ä½¿ç”¨æœ¬åŠŸèƒ½ã€‚" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp @@ -5785,31 +5757,25 @@ msgstr "选择模å¼" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Drag: Rotate selected node around pivot." -msgstr "移除选ä¸çš„节点或过渡动画。" +msgstr "拖动:围绕ä¸å¿ƒç‚¹æ—‹è½¬æ‰€é€‰èŠ‚ç‚¹ã€‚" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Alt+Drag: Move selected node." -msgstr "Alt+拖动:移动" +msgstr "Alt+拖动:移动所选节点。" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "V: Set selected node's pivot position." -msgstr "移除选ä¸çš„节点或过渡动画。" +msgstr "V:设置所选节点的ä¸å¿ƒç‚¹ä½ç½®ã€‚" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Alt+RMB: Show list of all nodes at position clicked, including locked." -msgstr "" -"æ˜¾ç¤ºé¼ æ ‡ç‚¹å‡»ä½ç½®çš„æ‰€æœ‰èŠ‚ç‚¹\n" -"ï¼ˆåŒ Alt + é¼ æ ‡å³é”®ï¼‰ã€‚" +msgstr "Alt+å³é”®ï¼šæ˜¾ç¤ºç‚¹å‡»ä½ç½®çš„æ‰€æœ‰èŠ‚ç‚¹åˆ—è¡¨ï¼ŒåŒ…å«å·²é”定节点。" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "RMB: Add node at position clicked." -msgstr "" +msgstr "å³é”®ï¼šåœ¨ç‚¹å‡»ä½ç½®æ·»åŠ èŠ‚ç‚¹ã€‚" #: editor/plugins/canvas_item_editor_plugin.cpp #: editor/plugins/spatial_editor_plugin.cpp @@ -6045,14 +6011,12 @@ msgid "Clear Pose" msgstr "清除姿势" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Add Node Here" -msgstr "æ·»åŠ èŠ‚ç‚¹" +msgstr "在æ¤å¤„æ·»åŠ èŠ‚ç‚¹" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Instance Scene Here" -msgstr "实例化场景" +msgstr "在æ¤å¤„实例化场景" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Multiply grid step by 2" @@ -6068,49 +6032,43 @@ msgstr "平移视图" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 3.125%" -msgstr "" +msgstr "缩放至 3.125%" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 6.25%" -msgstr "" +msgstr "缩放至 6.25%" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 12.5%" -msgstr "" +msgstr "缩放至 12.5%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 25%" -msgstr "缩å°" +msgstr "缩放至 25%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 50%" -msgstr "缩å°" +msgstr "缩放至 50%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 100%" -msgstr "缩å°" +msgstr "缩放至 100%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 200%" -msgstr "缩å°" +msgstr "缩放至 200%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 400%" -msgstr "缩å°" +msgstr "缩放至 400%" #: editor/plugins/canvas_item_editor_plugin.cpp -#, fuzzy msgid "Zoom to 800%" -msgstr "缩å°" +msgstr "缩放至 800%" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Zoom to 1600%" -msgstr "" +msgstr "缩放至 1600%" #: editor/plugins/canvas_item_editor_plugin.cpp msgid "Add %s" @@ -6355,9 +6313,8 @@ msgid "Couldn't create a single convex collision shape." msgstr "æ— æ³•åˆ›å»ºå•一凸碰撞形状。" #: editor/plugins/mesh_instance_editor_plugin.cpp -#, fuzzy msgid "Create Simplified Convex Shape" -msgstr "创建å•一凸形状" +msgstr "创建简化凸形状" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Create Single Convex Shape" @@ -6392,9 +6349,8 @@ msgid "No mesh to debug." msgstr "没有å¯è°ƒè¯•çš„ç½‘æ ¼ã€‚" #: editor/plugins/mesh_instance_editor_plugin.cpp -#, fuzzy msgid "Mesh has no UV in layer %d." -msgstr "模型在æ¤å±‚上没有 UV" +msgstr "ç½‘æ ¼åœ¨å±‚ %d 上没有 UV。" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "MeshInstance lacks a Mesh!" @@ -6458,9 +6414,8 @@ msgstr "" "这是最快(但是最ä¸ç²¾ç¡®ï¼‰çš„碰撞检测手段。" #: editor/plugins/mesh_instance_editor_plugin.cpp -#, fuzzy msgid "Create Simplified Convex Collision Sibling" -msgstr "创建å•一凸碰撞åŒçº§" +msgstr "创建简化凸碰撞åŒçº§" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "" @@ -6468,20 +6423,21 @@ msgid "" "This is similar to single collision shape, but can result in a simpler " "geometry in some cases, at the cost of accuracy." msgstr "" +"创建简化凸碰撞åŒçº§ã€‚\n" +"与å•一碰撞形状类似,但æŸäº›æƒ…况下产生的形状更简å•,代价是牺牲准确性。" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Create Multiple Convex Collision Siblings" msgstr "创建多个凸碰撞åŒçº§" #: editor/plugins/mesh_instance_editor_plugin.cpp -#, fuzzy msgid "" "Creates a polygon-based collision shape.\n" "This is a performance middle-ground between a single convex collision and a " "polygon-based collision." msgstr "" "创建基于多边形的碰撞形状。\n" -"这是性能ä½äºŽä¸Šè¿°ä¸¤ç§ä¹‹é—´çš„碰撞检测手段。" +"性能ä½äºŽå•一凸形碰撞和多边形碰撞之间。" #: editor/plugins/mesh_instance_editor_plugin.cpp msgid "Create Outline Mesh..." @@ -7117,24 +7073,20 @@ msgid "ResourcePreloader" msgstr "é¢„åŠ è½½èµ„æº" #: editor/plugins/room_manager_editor_plugin.cpp -#, fuzzy msgid "Flip Portals" -msgstr "水平翻转" +msgstr "翻转门户" #: editor/plugins/room_manager_editor_plugin.cpp -#, fuzzy msgid "Room Generate Points" -msgstr "生æˆé¡¶ç‚¹è®¡æ•°:" +msgstr "房间生æˆç‚¹" #: editor/plugins/room_manager_editor_plugin.cpp -#, fuzzy msgid "Generate Points" -msgstr "生æˆé¡¶ç‚¹è®¡æ•°:" +msgstr "生æˆç‚¹" #: editor/plugins/room_manager_editor_plugin.cpp -#, fuzzy msgid "Flip Portal" -msgstr "水平翻转" +msgstr "翻转门户" #: editor/plugins/root_motion_editor_plugin.cpp msgid "AnimationTree has no path set to an AnimationPlayer" @@ -7692,20 +7644,17 @@ msgid "None" msgstr "æ— " #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Rotate" -msgstr "å·ž(State)" +msgstr "旋转" #. TRANSLATORS: This refers to the movement that changes the position of an object. #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Translate" -msgstr "移动:" +msgstr "平移" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Scale" -msgstr "缩放:" +msgstr "缩放" #: editor/plugins/spatial_editor_plugin.cpp msgid "Scaling: " @@ -7728,52 +7677,44 @@ msgid "Animation Key Inserted." msgstr "æ’入动画键。" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Pitch:" -msgstr "俯仰角" +msgstr "俯仰角:" #: editor/plugins/spatial_editor_plugin.cpp msgid "Yaw:" -msgstr "" +msgstr "å航角:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Size:" -msgstr "大å°ï¼š " +msgstr "大å°ï¼š" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Objects Drawn:" -msgstr "绘制对象" +msgstr "绘制对象:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Material Changes:" -msgstr "æè´¨å˜æ›´" +msgstr "æè´¨å˜æ›´ï¼š" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Shader Changes:" -msgstr "ç€è‰²å™¨å˜æ›´" +msgstr "ç€è‰²å™¨å˜æ›´ï¼š" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Surface Changes:" -msgstr "表é¢å˜æ›´" +msgstr "表é¢å˜æ›´ï¼š" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Draw Calls:" -msgstr "绘制调用" +msgstr "绘制调用:" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Vertices:" -msgstr "顶点" +msgstr "顶点:" #: editor/plugins/spatial_editor_plugin.cpp msgid "FPS: %d (%s ms)" -msgstr "" +msgstr "FPS:%d(%s 毫秒)" #: editor/plugins/spatial_editor_plugin.cpp msgid "Top View." @@ -7928,9 +7869,8 @@ msgid "Freelook Slow Modifier" msgstr "缓慢自由视图速度" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Toggle Camera Preview" -msgstr "ä¿®æ”¹æ‘„åƒæœºå°ºå¯¸" +msgstr "å¼€å…³æ‘„åƒæœºé¢„览" #: editor/plugins/spatial_editor_plugin.cpp msgid "View Rotation Locked" @@ -7950,9 +7890,8 @@ msgstr "" "ä¸èƒ½å馈出实际游æˆä¸çš„æ€§èƒ½ã€‚" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Convert Rooms" -msgstr "转æ¢ä¸º %s" +msgstr "è½¬æ¢æˆ¿é—´" #: editor/plugins/spatial_editor_plugin.cpp msgid "XForm Dialog" @@ -7973,7 +7912,6 @@ msgstr "" "åŠç眼:Gizmo 也å¯ç©¿è¿‡ä¸é€æ˜Žçš„表é¢å¯è§ï¼ˆâ€œX å…‰â€ï¼‰ã€‚" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "Snap Nodes to Floor" msgstr "将节点å¸é™„至地é¢" @@ -7991,7 +7929,7 @@ msgstr "使用å¸é™„" #: editor/plugins/spatial_editor_plugin.cpp msgid "Converts rooms for portal culling." -msgstr "" +msgstr "ä¸ºé—¨æˆ·å‰”é™¤è½¬æ¢æˆ¿é—´ã€‚" #: editor/plugins/spatial_editor_plugin.cpp msgid "Bottom View" @@ -8087,9 +8025,8 @@ msgid "View Grid" msgstr "æ˜¾ç¤ºç½‘æ ¼" #: editor/plugins/spatial_editor_plugin.cpp -#, fuzzy msgid "View Portal Culling" -msgstr "视å£è®¾ç½®" +msgstr "显示门户剔除" #: editor/plugins/spatial_editor_plugin.cpp #: modules/gridmap/grid_map_editor_plugin.cpp @@ -8409,221 +8346,194 @@ msgid "TextureRegion" msgstr "纹ç†åŒºåŸŸ" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Colors" msgstr "颜色" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Fonts" msgstr "å—体" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Icons" msgstr "å›¾æ ‡" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Styleboxes" -msgstr "æ ·å¼" +msgstr "æ ·å¼ç›’" #: editor/plugins/theme_editor_plugin.cpp msgid "{num} color(s)" -msgstr "" +msgstr "{num} 个颜色" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No colors found." -msgstr "找ä¸åˆ°å资æºã€‚" +msgstr "没有颜色。" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "{num} constant(s)" -msgstr "常é‡" +msgstr "{num} 个常é‡" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No constants found." -msgstr "颜色常é‡ã€‚" +msgstr "没有常é‡ã€‚" #: editor/plugins/theme_editor_plugin.cpp msgid "{num} font(s)" -msgstr "" +msgstr "{num} 个å—体" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No fonts found." -msgstr "未找到ï¼" +msgstr "没有å—体。" #: editor/plugins/theme_editor_plugin.cpp msgid "{num} icon(s)" -msgstr "" +msgstr "{num} ä¸ªå›¾æ ‡" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No icons found." -msgstr "未找到ï¼" +msgstr "æ²¡æœ‰å›¾æ ‡ã€‚" #: editor/plugins/theme_editor_plugin.cpp msgid "{num} stylebox(es)" -msgstr "" +msgstr "{num} ä¸ªæ ·å¼ç›’" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "No styleboxes found." -msgstr "找ä¸åˆ°å资æºã€‚" +msgstr "æ²¡æœ‰æ ·å¼ç›’。" #: editor/plugins/theme_editor_plugin.cpp msgid "{num} currently selected" -msgstr "" +msgstr "当å‰é€‰ä¸ {num} 个" #: editor/plugins/theme_editor_plugin.cpp msgid "Nothing was selected for the import." -msgstr "" +msgstr "没有选ä¸å¯¼å…¥ä»»ä½•东西。" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Importing Theme Items" -msgstr "导入主题" +msgstr "æ£åœ¨å¯¼å…¥ä¸»é¢˜é¡¹ç›®" #: editor/plugins/theme_editor_plugin.cpp msgid "Importing items {n}/{n}" -msgstr "" +msgstr "æ£åœ¨å¯¼å…¥é¡¹ç›® {n}/{n}" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Updating the editor" -msgstr "确定è¦é€€å‡ºç¼–辑器å—?" +msgstr "æ£åœ¨æ›´æ–°ç¼–辑器" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Finalizing" -msgstr "æ£åœ¨åˆ†æž" +msgstr "æ£åœ¨æ”¶å°¾" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Filter:" -msgstr "过滤: " +msgstr "ç›é€‰ï¼š" #: editor/plugins/theme_editor_plugin.cpp msgid "With Data" -msgstr "" +msgstr "嫿•°æ®" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select by data type:" -msgstr "选择一个节点" +msgstr "按数æ®ç±»åž‹é€‰æ‹©ï¼š" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible color items." -msgstr "选择一个拆分以擦除它。" +msgstr "选择所有å¯è§çš„颜色项目。" #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible color items and their data." -msgstr "" +msgstr "选择所有å¯è§çš„颜色项目和它们的数æ®ã€‚" #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all visible color items." -msgstr "" +msgstr "å–æ¶ˆé€‰æ‹©æ‰€æœ‰å¯è§çš„颜色项目。" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible constant items." -msgstr "请先选择一个设置项ï¼" +msgstr "选择所有å¯è§çš„常é‡é¡¹ç›®ã€‚" #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible constant items and their data." -msgstr "" +msgstr "选择所有å¯è§çš„常é‡é¡¹ç›®å’Œå®ƒä»¬çš„æ•°æ®ã€‚" #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all visible constant items." -msgstr "" +msgstr "å–æ¶ˆé€‰æ‹©æ‰€æœ‰å¯è§çš„常é‡é¡¹ç›®ã€‚" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible font items." -msgstr "请先选择一个设置项ï¼" +msgstr "选择所有å¯è§çš„å—体项目。" #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible font items and their data." -msgstr "" +msgstr "选择所有å¯è§çš„å—体项目和它们的数æ®ã€‚" #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all visible font items." -msgstr "" +msgstr "å–æ¶ˆé€‰æ‹©æ‰€æœ‰å¯è§çš„å—体项目。" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible icon items." -msgstr "请先选择一个设置项ï¼" +msgstr "选择所有å¯è§çš„å›¾æ ‡é¡¹ç›®ã€‚" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all visible icon items and their data." -msgstr "请先选择一个设置项ï¼" +msgstr "选择所有å¯è§çš„å›¾æ ‡é¡¹ç›®å’Œå®ƒä»¬çš„æ•°æ®ã€‚" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Deselect all visible icon items." -msgstr "请先选择一个设置项ï¼" +msgstr "å–æ¶ˆé€‰æ‹©æ‰€æœ‰å¯è§çš„å›¾æ ‡é¡¹ç›®ã€‚" #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible stylebox items." -msgstr "" +msgstr "选择所有å¯è§çš„æ ·å¼ç›’项目。" #: editor/plugins/theme_editor_plugin.cpp msgid "Select all visible stylebox items and their data." -msgstr "" +msgstr "选择所有å¯è§çš„æ ·å¼ç›’项目和它们的数æ®ã€‚" #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all visible stylebox items." -msgstr "" +msgstr "å–æ¶ˆé€‰æ‹©æ‰€æœ‰å¯è§çš„æ ·å¼ç›’项目。" #: editor/plugins/theme_editor_plugin.cpp msgid "" "Caution: Adding icon data may considerably increase the size of your Theme " "resource." -msgstr "" +msgstr "注æ„ï¼šæ·»åŠ å›¾æ ‡æ•°æ®å¯èƒ½æ˜¾è‘—å¢žåŠ ä¸»é¢˜èµ„æºçš„大å°ã€‚" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Collapse types." -msgstr "全部折å " +msgstr "折å 类型。" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Expand types." -msgstr "全部展开" +msgstr "展开类型。" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select all Theme items." -msgstr "é€‰æ‹©æ¨¡æ¿æ–‡ä»¶" +msgstr "选择所有主题项目。" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select With Data" -msgstr "选择顶点" +msgstr "é€‰æ‹©å«æ•°æ®" #: editor/plugins/theme_editor_plugin.cpp msgid "Select all Theme items with item data." -msgstr "" +msgstr "选择所有主题项目åŠé¡¹ç›®æ•°æ®ã€‚" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Deselect All" -msgstr "全选" +msgstr "å–æ¶ˆå…¨é€‰" #: editor/plugins/theme_editor_plugin.cpp msgid "Deselect all Theme items." -msgstr "" +msgstr "å–æ¶ˆé€‰æ‹©æ‰€æœ‰ä¸»é¢˜é¡¹ç›®ã€‚" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Import Selected" -msgstr "导入场景" +msgstr "导入所选项" #: editor/plugins/theme_editor_plugin.cpp msgid "" @@ -8631,271 +8541,241 @@ msgid "" "closing this window.\n" "Close anyway?" msgstr "" +"导入项目选项å¡è¿˜æœ‰é€‰ä¸çš„é¡¹ç›®ã€‚å…³é—æœ¬çª—å£ä¼šä¸¢å¤±é€‰é¡¹ã€‚\n" +"ä»ç„¶å…³é—å—?" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All Color Items" -msgstr "移除所有项目" +msgstr "移除所有颜色项目" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Item" -msgstr "移除项目" +msgstr "é‡å‘½å项目" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All Constant Items" -msgstr "移除所有项目" +msgstr "移除所有常é‡é¡¹ç›®" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All Font Items" -msgstr "移除所有项目" +msgstr "移除所有å—体项目" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All Icon Items" -msgstr "移除所有项目" +msgstr "ç§»é™¤æ‰€æœ‰å›¾æ ‡é¡¹ç›®" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove All StyleBox Items" -msgstr "移除所有项目" +msgstr "ç§»é™¤æ‰€æœ‰æ ·å¼ç›’项目" + +#: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Color Item" -msgstr "æ·»åŠ ç±»é¡¹ç›®" +msgstr "æ·»åŠ é¢œè‰²é¡¹ç›®" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Constant Item" -msgstr "æ·»åŠ ç±»é¡¹ç›®" +msgstr "æ·»åŠ å¸¸é‡é¡¹ç›®" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Font Item" -msgstr "æ·»åŠ é¡¹ç›®" +msgstr "æ·»åŠ å—体项目" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Icon Item" -msgstr "æ·»åŠ é¡¹ç›®" +msgstr "æ·»åŠ å›¾æ ‡é¡¹ç›®" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Stylebox Item" -msgstr "æ·»åŠ æ‰€æœ‰é¡¹ç›®" +msgstr "æ·»åŠ æ ·å¼ç›’项目" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Color Item" -msgstr "移除类项目" +msgstr "é‡å‘½å颜色项目" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Constant Item" -msgstr "移除类项目" +msgstr "é‡å‘½å常é‡é¡¹ç›®" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Font Item" -msgstr "é‡å‘½å节点" +msgstr "é‡å‘½åå—体项目" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Icon Item" -msgstr "é‡å‘½å节点" +msgstr "é‡å‘½åå›¾æ ‡é¡¹ç›®" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Rename Stylebox Item" -msgstr "移除选ä¸é¡¹ç›®" +msgstr "é‡å‘½åæ ·å¼ç›’项目" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Invalid file, not a Theme resource." -msgstr "æ— æ•ˆæ–‡ä»¶ï¼Œä¸æ˜¯éŸ³é¢‘总线布局。" +msgstr "æ— æ•ˆæ–‡ä»¶ï¼Œä¸æ˜¯ Theme 资æºã€‚" #: editor/plugins/theme_editor_plugin.cpp msgid "Invalid file, same as the edited Theme resource." -msgstr "" +msgstr "æ— æ•ˆæ–‡ä»¶ï¼Œä¸Žæ£åœ¨ç¼–辑的 Theme 资æºç›¸åŒã€‚" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Manage Theme Items" -msgstr "ç®¡ç†æ¨¡æ¿" +msgstr "管ç†ä¸»é¢˜é¡¹ç›®" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Edit Items" -msgstr "å¯ç¼–辑的项目" +msgstr "编辑项目" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Types:" msgstr "类型:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Type:" -msgstr "类型:" +msgstr "æ·»åŠ ç±»åž‹ï¼š" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Item:" -msgstr "æ·»åŠ é¡¹ç›®" +msgstr "æ·»åŠ é¡¹ç›®ï¼š" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add StyleBox Item" -msgstr "æ·»åŠ æ‰€æœ‰é¡¹ç›®" +msgstr "æ·»åŠ æ ·å¼ç›’项目" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove Items:" -msgstr "移除项目" +msgstr "移除项目:" #: editor/plugins/theme_editor_plugin.cpp msgid "Remove Class Items" msgstr "移除类项目" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Remove Custom Items" -msgstr "移除类项目" +msgstr "移除自定义项目" #: editor/plugins/theme_editor_plugin.cpp msgid "Remove All Items" msgstr "移除所有项目" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Theme Item" -msgstr "GUI主题项目" +msgstr "æ·»åŠ ä¸»é¢˜é¡¹ç›®" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Old Name:" -msgstr "节点å称:" +msgstr "æ—§å称:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Import Items" -msgstr "导入主题" +msgstr "导入项目" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Default Theme" -msgstr "默认" +msgstr "默认主题" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Editor Theme" -msgstr "编辑主题" +msgstr "编辑器主题" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select Another Theme Resource:" -msgstr "åˆ é™¤èµ„æº" +msgstr "选择其它主题资æºï¼š" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Another Theme" -msgstr "导入主题" +msgstr "其它主题" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Confirm Item Rename" -msgstr "é‡å‘½å轨é“" +msgstr "确认项目é‡å‘½å" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Cancel Item Rename" -msgstr "批é‡é‡å‘½å" +msgstr "å–æ¶ˆé¡¹ç›®é‡å‘½å" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Override Item" -msgstr "é‡å†™" +msgstr "覆盖项目" #: editor/plugins/theme_editor_plugin.cpp msgid "Unpin this StyleBox as a main style." -msgstr "" +msgstr "å–æ¶ˆå°†æ¤æ ·å¼ç›’ç½®é¡¶ä¸ºä¸»æ ·å¼ã€‚" #: editor/plugins/theme_editor_plugin.cpp msgid "" "Pin this StyleBox as a main style. Editing its properties will update the " "same properties in all other StyleBoxes of this type." msgstr "" +"å°†æ¤æ ·å¼ç›’ç½®é¡¶ä¸ºä¸»æ ·å¼ã€‚ç¼–è¾‘å…¶å±žæ€§ä¼šæ›´æ–°è¯¥ç±»åž‹ä¸‹å…¶å®ƒæ‰€æœ‰æ ·å¼ç›’的相åŒå±žæ€§ã€‚" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Type" -msgstr "类型" +msgstr "æ·»åŠ ç±»åž‹" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Item Type" -msgstr "æ·»åŠ é¡¹ç›®" +msgstr "æ·»åŠ é¡¹ç›®ç±»åž‹" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Node Types:" -msgstr "节点类型" +msgstr "节点类型:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Show Default" -msgstr "åŠ è½½é»˜è®¤" +msgstr "显示默认" #: editor/plugins/theme_editor_plugin.cpp msgid "Show default type items alongside items that have been overridden." -msgstr "" +msgstr "将默认类型项目与覆盖åŽçš„一起显示。" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Override All" -msgstr "é‡å†™" +msgstr "全部覆盖" #: editor/plugins/theme_editor_plugin.cpp msgid "Override all default type items." -msgstr "" +msgstr "覆盖所有默认类型项目。" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Theme:" -msgstr "主题" +msgstr "主题:" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Manage Items..." -msgstr "管ç†å¯¼å‡ºæ¨¡æ¿..." +msgstr "管ç†é¡¹ç›®..." #: editor/plugins/theme_editor_plugin.cpp msgid "Add, remove, organize and import Theme items." -msgstr "" +msgstr "æ·»åŠ ã€ç§»é™¤ã€ç»„织和导入 Theme 项目。" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Add Preview" -msgstr "预览" +msgstr "æ·»åŠ é¢„è§ˆ" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Default Preview" -msgstr "更新预览" +msgstr "默认预览" #: editor/plugins/theme_editor_plugin.cpp -#, fuzzy msgid "Select UI Scene:" -msgstr "选择æºç½‘æ ¼ï¼š" +msgstr "选择 UI 场景:" #: editor/plugins/theme_editor_preview.cpp msgid "" "Toggle the control picker, allowing to visually select control types for " "edit." -msgstr "" +msgstr "开关控件拾å–器,å¯ä»¥å¯è§†åŒ–地选择所需编辑的控件类型。" #: editor/plugins/theme_editor_preview.cpp msgid "Toggle Button" @@ -8930,7 +8810,6 @@ msgid "Checked Radio Item" msgstr "已选å•选项目" #: editor/plugins/theme_editor_preview.cpp -#, fuzzy msgid "Named Separator" msgstr "带å称的分隔线" @@ -8984,20 +8863,19 @@ msgstr "有, 很多, 选项" #: editor/plugins/theme_editor_preview.cpp msgid "Invalid path, the PackedScene resource was probably moved or removed." -msgstr "" +msgstr "æ— æ•ˆè·¯å¾„ï¼ŒPackedScene 资æºå¯èƒ½å·²è¢«ç§»åŠ¨æˆ–ç§»é™¤ã€‚" #: editor/plugins/theme_editor_preview.cpp msgid "Invalid PackedScene resource, must have a Control node at its root." -msgstr "" +msgstr "æ— æ•ˆ PackedScene 资æºï¼Œæ ¹èŠ‚ç‚¹å¿…é¡»æ˜¯ Control 节点。" #: editor/plugins/theme_editor_preview.cpp -#, fuzzy msgid "Invalid file, not a PackedScene resource." -msgstr "æ— æ•ˆæ–‡ä»¶ï¼Œä¸æ˜¯éŸ³é¢‘总线布局。" +msgstr "æ— æ•ˆæ–‡ä»¶ï¼Œä¸æ˜¯ PackedScene 资æºã€‚" #: editor/plugins/theme_editor_preview.cpp msgid "Reload the scene to reflect its most actual state." -msgstr "" +msgstr "釿–°åŠ è½½åœºæ™¯ï¼Œåæ˜ 最新状æ€ã€‚" #: editor/plugins/tile_map_editor_plugin.cpp msgid "Erase Selection" @@ -10354,9 +10232,8 @@ msgid "VisualShader" msgstr "å¯è§†ç€è‰²å™¨" #: editor/plugins/visual_shader_editor_plugin.cpp -#, fuzzy msgid "Edit Visual Property:" -msgstr "编辑å¯è§†å±žæ€§" +msgstr "编辑å¯è§†å±žæ€§ï¼š" #: editor/plugins/visual_shader_editor_plugin.cpp msgid "Visual Shader Mode Changed" @@ -10480,9 +10357,8 @@ msgid "Script" msgstr "脚本" #: editor/project_export.cpp -#, fuzzy msgid "GDScript Export Mode:" -msgstr "脚本导出模å¼:" +msgstr "GDScript 导出模å¼ï¼š" #: editor/project_export.cpp msgid "Text" @@ -10490,21 +10366,19 @@ msgstr "文本" #: editor/project_export.cpp msgid "Compiled Bytecode (Faster Loading)" -msgstr "" +msgstr "编译åŽçš„å—节ç ï¼ˆåŠ è½½æ›´å¿«ï¼‰" #: editor/project_export.cpp msgid "Encrypted (Provide Key Below)" msgstr "åŠ å¯†ï¼ˆåœ¨ä¸‹é¢æä¾›å¯†é’¥ï¼‰" #: editor/project_export.cpp -#, fuzzy msgid "Invalid Encryption Key (must be 64 hexadecimal characters long)" -msgstr "æ— æ•ˆçš„åŠ å¯†å¯†é’¥ï¼ˆé•¿åº¦å¿…é¡»ä¸º 64 个å—符)" +msgstr "æ— æ•ˆçš„åŠ å¯†å¯†é’¥ï¼ˆé•¿åº¦å¿…é¡»ä¸º 64 个åå…进制å—符)" #: editor/project_export.cpp -#, fuzzy msgid "GDScript Encryption Key (256-bits as hexadecimal):" -msgstr "è„šæœ¬åŠ å¯†å¯†é’¥ï¼ˆ256 ä½ 16 进制ç ):" +msgstr "GDScript åŠ å¯†å¯†é’¥ï¼ˆ256 ä½åå…进制ç ):" #: editor/project_export.cpp msgid "Export PCK/Zip" @@ -10576,7 +10450,6 @@ msgid "Imported Project" msgstr "已导入的项目" #: editor/project_manager.cpp -#, fuzzy msgid "Invalid project name." msgstr "项目åç§°æ— æ•ˆã€‚" @@ -10792,14 +10665,12 @@ msgid "Are you sure to run %d projects at once?" msgstr "确定è¦åŒæ—¶è¿è¡Œ %d 个项目å—?" #: editor/project_manager.cpp -#, fuzzy msgid "Remove %d projects from the list?" -msgstr "从列表ä¸é€‰æ‹©è®¾å¤‡" +msgstr "是å¦ä»Žåˆ—表ä¸ç§»é™¤ %d 个项目?" #: editor/project_manager.cpp -#, fuzzy msgid "Remove this project from the list?" -msgstr "从列表ä¸é€‰æ‹©è®¾å¤‡" +msgstr "是å¦ä»Žåˆ—表ä¸ç§»é™¤è¯¥é¡¹ç›®ï¼Ÿ" #: editor/project_manager.cpp msgid "" @@ -10831,9 +10702,8 @@ msgid "Project Manager" msgstr "项目管ç†å™¨" #: editor/project_manager.cpp -#, fuzzy msgid "Local Projects" -msgstr "项目" +msgstr "本地项目" #: editor/project_manager.cpp msgid "Loading, please wait..." @@ -10844,23 +10714,20 @@ msgid "Last Modified" msgstr "修改时间" #: editor/project_manager.cpp -#, fuzzy msgid "Edit Project" -msgstr "导出项目" +msgstr "编辑项目" #: editor/project_manager.cpp -#, fuzzy msgid "Run Project" -msgstr "é‡å‘½å项目" +msgstr "è¿è¡Œé¡¹ç›®" #: editor/project_manager.cpp msgid "Scan" msgstr "扫æ" #: editor/project_manager.cpp -#, fuzzy msgid "Scan Projects" -msgstr "项目" +msgstr "扫æé¡¹ç›®" #: editor/project_manager.cpp msgid "Select a Folder to Scan" @@ -10871,14 +10738,12 @@ msgid "New Project" msgstr "新建项目" #: editor/project_manager.cpp -#, fuzzy msgid "Import Project" -msgstr "已导入的项目" +msgstr "导入项目" #: editor/project_manager.cpp -#, fuzzy msgid "Remove Project" -msgstr "é‡å‘½å项目" +msgstr "移除项目" #: editor/project_manager.cpp msgid "Remove Missing" @@ -10889,9 +10754,8 @@ msgid "About" msgstr "关于" #: editor/project_manager.cpp -#, fuzzy msgid "Asset Library Projects" -msgstr "ç´ æåº“" +msgstr "ç´ æåº“项目" #: editor/project_manager.cpp msgid "Restart Now" @@ -10903,7 +10767,7 @@ msgstr "移除全部" #: editor/project_manager.cpp msgid "Also delete project contents (no undo!)" -msgstr "" +msgstr "åŒæ—¶åˆ é™¤é¡¹ç›®å†…å®¹ï¼ˆæ— æ³•æ’¤é”€ï¼ï¼‰" #: editor/project_manager.cpp msgid "Can't run project" @@ -10918,19 +10782,17 @@ msgstr "" "æ˜¯å¦æŸ¥çœ‹ç´ æåº“ä¸çš„官方示例项目?" #: editor/project_manager.cpp -#, fuzzy msgid "Filter projects" -msgstr "ç›é€‰å±žæ€§" +msgstr "ç›é€‰é¡¹ç›®" #: editor/project_manager.cpp -#, fuzzy msgid "" "This field filters projects by name and last path component.\n" "To filter projects by name and full path, the query must contain at least " "one `/` character." msgstr "" -"æœç´¢æ¡†æ ¹æ®å称和路径的末尾部分æ¥è¿‡æ»¤é¡¹ç›®ã€‚\n" -"å¦‚æžœè¦æ ¹æ®å称和完整路径过滤,æœç´¢å†…容应至少包å«ä¸€ä¸ª `/` å—符。" +"该æœç´¢æ¡†æ ¹æ®å称和路径的末尾部分æ¥ç›é€‰é¡¹ç›®ã€‚\n" +"å¦‚æžœè¦æ ¹æ®å称和完整路径ç›é€‰ï¼Œæœç´¢å†…容应至少包å«ä¸€ä¸ªâ€œ/â€å—符。" #: editor/project_settings_editor.cpp msgid "Key " @@ -10938,7 +10800,7 @@ msgstr "按键 " #: editor/project_settings_editor.cpp msgid "Physical Key" -msgstr "" +msgstr "ç‰©ç†æŒ‰é”®" #: editor/project_settings_editor.cpp msgid "Joy Button" @@ -10984,7 +10846,7 @@ msgstr "设备" #: editor/project_settings_editor.cpp msgid " (Physical)" -msgstr "" +msgstr " (物ç†ï¼‰" #: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp msgid "Press a Key..." @@ -11126,23 +10988,20 @@ msgid "Override for Feature" msgstr "é‡å†™åŠŸèƒ½" #: editor/project_settings_editor.cpp -#, fuzzy msgid "Add %d Translations" -msgstr "æ·»åŠ ç¿»è¯‘" +msgstr "æ·»åŠ %d 个翻译" #: editor/project_settings_editor.cpp msgid "Remove Translation" msgstr "移除翻译" #: editor/project_settings_editor.cpp -#, fuzzy msgid "Translation Resource Remap: Add %d Path(s)" -msgstr "æ·»åŠ èµ„æºé‡å®šå‘" +msgstr "翻译资æºé‡å®šå‘ï¼šæ·»åŠ %d 个路径" #: editor/project_settings_editor.cpp -#, fuzzy msgid "Translation Resource Remap: Add %d Remap(s)" -msgstr "æ·»åŠ èµ„æºé‡å®šå‘" +msgstr "翻译资æºé‡å®šå‘ï¼šæ·»åŠ %d 个é‡å®šå‘" #: editor/project_settings_editor.cpp msgid "Change Resource Remap Language" @@ -11581,13 +11440,13 @@ msgstr "是å¦åˆ 除节点 “%sâ€ï¼Ÿ" #: editor/scene_tree_dock.cpp msgid "" "Saving the branch as a scene requires having a scene open in the editor." -msgstr "" +msgstr "将分支ä¿å˜ä¸ºåœºæ™¯éœ€è¦ç¼–辑器打开场景。" #: editor/scene_tree_dock.cpp msgid "" "Saving the branch as a scene requires selecting only one node, but you have " "selected %d nodes." -msgstr "" +msgstr "将分支ä¿å˜ä¸ºåœºæ™¯éœ€è¦ä»…é€‰æ‹©ä¸€ä¸ªèŠ‚ç‚¹ï¼Œä½†ä½ é€‰äº† %d 个。" #: editor/scene_tree_dock.cpp msgid "" @@ -11596,6 +11455,9 @@ msgid "" "FileSystem dock context menu\n" "or create an inherited scene using Scene > New Inherited Scene... instead." msgstr "" +"æ— æ³•å°†æ ¹èŠ‚ç‚¹ä¿å˜ä¸ºå®žä¾‹åŒ–节点。\n" +"如果è¦åˆ›å»ºå½“å‰åœºæ™¯çš„å¯ç¼–è¾‘å‰¯æœ¬ï¼Œè¯·åœ¨æ–‡ä»¶ç³»ç»Ÿé¢æ¿çš„上下文èœå•ä¸å¤åˆ¶\n" +"或者使用场景 > 新建继承场景... 创建继承场景。" #: editor/scene_tree_dock.cpp msgid "" @@ -11603,6 +11465,9 @@ msgid "" "To create a variation of a scene, you can make an inherited scene based on " "the instanced scene using Scene > New Inherited Scene... instead." msgstr "" +"æ— æ³•ä¿å˜å®žä¾‹åŒ–场景分支。\n" +"如果è¦åˆ›å»ºåœºæ™¯çš„å˜ç§ï¼Œè¯·ä½¿ç”¨åœºæ™¯ > 新建继承场景... 创建基于该实例化场景的继承" +"场景。" #: editor/scene_tree_dock.cpp msgid "Save New Scene As..." @@ -11998,7 +11863,7 @@ msgstr "注æ„ï¼šå†…ç½®è„šæœ¬æœ‰å…¶å±€é™æ€§ï¼Œå¹¶ä¸”ä¸èƒ½ä½¿ç”¨å¤–部编辑器 msgid "" "Warning: Having the script name be the same as a built-in type is usually " "not desired." -msgstr "" +msgstr "è¦å‘Šï¼šè„šæœ¬å称通常ä¸èƒ½ä¸Žå†…置类型一致。" #: editor/script_create_dialog.cpp msgid "Class Name:" @@ -12070,7 +11935,7 @@ msgstr "å¤åˆ¶é”™è¯¯ä¿¡æ¯" #: editor/script_editor_debugger.cpp msgid "Open C++ Source on GitHub" -msgstr "" +msgstr "在 GitHub 打开 C++ æºç " #: editor/script_editor_debugger.cpp msgid "Video RAM" @@ -12142,7 +12007,7 @@ msgstr "æ ¼å¼" #: editor/script_editor_debugger.cpp msgid "Usage" -msgstr "用法" +msgstr "å 用" #: editor/script_editor_debugger.cpp msgid "Misc" @@ -12248,6 +12113,16 @@ msgstr "修改圆柱体高度" msgid "Change Ray Shape Length" msgstr "修改射线形状长度" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "è®¾ç½®æ›²çº¿çš„é¡¶ç‚¹åæ ‡" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "è®¾ç½®æ›²çº¿çš„é¡¶ç‚¹åæ ‡" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "改å˜åœ†æŸ±ä½“åŠå¾„" @@ -12357,14 +12232,12 @@ msgid "Object can't provide a length." msgstr "å¯¹è±¡æ— æ³•æä¾›é•¿åº¦ã€‚" #: modules/gltf/editor_scene_exporter_gltf_plugin.cpp -#, fuzzy msgid "Export Mesh GLTF2" -msgstr "å¯¼å‡ºç½‘æ ¼åº“" +msgstr "å¯¼å‡ºç½‘æ ¼ GLTF2" #: modules/gltf/editor_scene_exporter_gltf_plugin.cpp -#, fuzzy msgid "Export GLTF..." -msgstr "导出..." +msgstr "导出 GLTF..." #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Next Plane" @@ -12404,12 +12277,11 @@ msgstr "ç½‘æ ¼åœ°å›¾ç²˜è´´æ‰€é€‰é¡¹" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "GridMap Paint" -msgstr "ç»˜åˆ¶æ …æ ¼å›¾" +msgstr "ç½‘æ ¼åœ°å›¾ç»˜åˆ¶" #: modules/gridmap/grid_map_editor_plugin.cpp -#, fuzzy msgid "GridMap Selection" -msgstr "ç½‘æ ¼åœ°å›¾å¡«å……æ‰€é€‰é¡¹" +msgstr "ç½‘æ ¼åœ°å›¾é€‰æ‹©" #: modules/gridmap/grid_map_editor_plugin.cpp msgid "Grid Map" @@ -12656,14 +12528,12 @@ msgid "Add Output Port" msgstr "å¢žåŠ è¾“å‡ºç«¯å£" #: modules/visual_script/visual_script_editor.cpp -#, fuzzy msgid "Change Port Type" -msgstr "更改类型" +msgstr "更改端å£ç±»åž‹" #: modules/visual_script/visual_script_editor.cpp -#, fuzzy msgid "Change Port Name" -msgstr "更改输入端å£åç§°" +msgstr "更改端å£åç§°" #: modules/visual_script/visual_script_editor.cpp msgid "Override an existing built-in function." @@ -12774,7 +12644,6 @@ msgid "Add Preload Node" msgstr "æ·»åŠ é¢„è½½ (Preload) 节点" #: modules/visual_script/visual_script_editor.cpp -#, fuzzy msgid "Add Node(s)" msgstr "æ·»åŠ èŠ‚ç‚¹" @@ -13034,37 +12903,31 @@ msgstr "从列表ä¸é€‰æ‹©è®¾å¤‡" #: platform/android/export/export.cpp msgid "Running on %s" -msgstr "" +msgstr "æ£è¿è¡ŒäºŽ %d" #: platform/android/export/export.cpp -#, fuzzy msgid "Exporting APK..." -msgstr "全部导出" +msgstr "æ£åœ¨å¯¼å‡º APK……" #: platform/android/export/export.cpp -#, fuzzy msgid "Uninstalling..." -msgstr "å¸è½½" +msgstr "æ£åœ¨å¸è½½â€¦â€¦" #: platform/android/export/export.cpp -#, fuzzy msgid "Installing to device, please wait..." -msgstr "æ£åœ¨åŠ è½½ï¼Œè¯·ç¨å€™â€¦â€¦" +msgstr "æ£åœ¨å®‰è£…到设备,请ç¨å€™â€¦â€¦" #: platform/android/export/export.cpp -#, fuzzy msgid "Could not install to device: %s" -msgstr "æ— æ³•å®žä¾‹åŒ–åœºæ™¯ï¼" +msgstr "æ— æ³•å®‰è£…åˆ°è®¾å¤‡ï¼š%s" #: platform/android/export/export.cpp -#, fuzzy msgid "Running on device..." -msgstr "执行自定义脚本..." +msgstr "æ£åœ¨è®¾å¤‡ä¸Šè¿è¡Œâ€¦â€¦" #: platform/android/export/export.cpp -#, fuzzy msgid "Could not execute on device." -msgstr "æ— æ³•åˆ›å»ºæ–‡ä»¶å¤¹ã€‚" +msgstr "æ— æ³•åœ¨è®¾å¤‡ä¸Šè¿è¡Œã€‚" #: platform/android/export/export.cpp msgid "Unable to find the 'apksigner' tool." @@ -13173,40 +13036,37 @@ msgid "" "directory.\n" "The resulting %s is unsigned." msgstr "" +"æ— æ³•æ‰¾åˆ°â€œapksignerâ€ã€‚\n" +"请检查 Android SDK çš„ build-tools ç›®å½•ä¸æ˜¯å¦æœ‰æ¤å‘½ä»¤ã€‚\n" +"生æˆçš„ %s 未ç¾å。" #: platform/android/export/export.cpp msgid "Signing debug %s..." -msgstr "" +msgstr "æ£åœ¨ç¾å调试 %s……" #: platform/android/export/export.cpp -#, fuzzy msgid "Signing release %s..." -msgstr "" -"æ£åœ¨æ‰«ææ–‡ä»¶ï¼Œ\n" -"请ç¨å€™â€¦â€¦" +msgstr "æ£åœ¨ç¾åå‘布 %s……" #: platform/android/export/export.cpp -#, fuzzy msgid "Could not find keystore, unable to export." -msgstr "æ— æ³•æ‰“å¼€å¯¼å‡ºæ¨¡æ¿ï¼š" +msgstr "找ä¸åˆ°å¯†é’¥åº“ï¼Œæ— æ³•å¯¼å‡ºã€‚" #: platform/android/export/export.cpp msgid "'apksigner' returned with error #%d" -msgstr "" +msgstr "“apksignerâ€è¿”回错误 #%d" #: platform/android/export/export.cpp -#, fuzzy msgid "Verifying %s..." -msgstr "æ£åœ¨æ·»åŠ %s..." +msgstr "æ£åœ¨æ ¡éªŒ %s……" #: platform/android/export/export.cpp msgid "'apksigner' verification of %s failed." -msgstr "" +msgstr "“apksignerâ€æ ¡éªŒ %s 失败。" #: platform/android/export/export.cpp -#, fuzzy msgid "Exporting for Android" -msgstr "全部导出" +msgstr "æ£åœ¨ä¸º Android 导出" #: platform/android/export/export.cpp msgid "Invalid filename! Android App Bundle requires the *.aab extension." @@ -13222,7 +13082,7 @@ msgstr "æ— æ•ˆæ–‡ä»¶åï¼Android APK 必须有 *.apk 扩展。" #: platform/android/export/export.cpp msgid "Unsupported export format!\n" -msgstr "" +msgstr "䏿”¯æŒçš„å¯¼å‡ºæ ¼å¼ï¼\n" #: platform/android/export/export.cpp msgid "" @@ -13246,17 +13106,15 @@ msgstr "" #: platform/android/export/export.cpp msgid "" "Unable to overwrite res://android/build/res/*.xml files with project name" -msgstr "" +msgstr "æ— æ³•ä½¿ç”¨é¡¹ç›®å称覆盖 res://android/build/res/*.xml 文件" #: platform/android/export/export.cpp -#, fuzzy msgid "Could not export project files to gradle project\n" -msgstr "æ— æ³•åœ¨é¡¹ç›®ç›®å½•ä¸‹æ‰¾åˆ°project.godot文件。" +msgstr "æ— æ³•å°†é¡¹ç›®æ–‡ä»¶å¯¼å‡ºè‡³ gradle 项目\n" #: platform/android/export/export.cpp -#, fuzzy msgid "Could not write expansion package file!" -msgstr "æ— æ³•å†™å…¥æ–‡ä»¶:" +msgstr "æ— æ³•å†™å…¥æ‰©å±•åŒ…æ–‡ä»¶ï¼" #: platform/android/export/export.cpp msgid "Building Android Project (gradle)" @@ -13281,21 +13139,20 @@ msgid "" msgstr "æ— æ³•å¤åˆ¶ä¸Žæ›´å导出文件,请在 Gradle 项目文件夹内确认输出。" #: platform/android/export/export.cpp -#, fuzzy msgid "Package not found: %s" -msgstr "没有动画: “%sâ€" +msgstr "包ä¸å˜åœ¨ï¼š%s" #: platform/android/export/export.cpp -#, fuzzy msgid "Creating APK..." -msgstr "æ£åœ¨åˆ›å»ºè½®å»“..." +msgstr "æ£åœ¨åˆ›å»º APK……" #: platform/android/export/export.cpp -#, fuzzy msgid "" "Could not find template APK to export:\n" "%s" -msgstr "æ— æ³•æ‰“å¼€å¯¼å‡ºæ¨¡æ¿ï¼š" +msgstr "" +"找ä¸åˆ°å¯¼å‡ºæ¨¡æ¿ APK:\n" +"%s" #: platform/android/export/export.cpp msgid "" @@ -13304,24 +13161,24 @@ msgid "" "Please build a template with all required libraries, or uncheck the missing " "architectures in the export preset." msgstr "" +"导出模æ¿ç¼ºå¤±æ‰€é€‰æž¶æž„的库:%s。\n" +"请使用全部所需的库构建模æ¿ï¼Œæˆ–者在导出预设ä¸å–消对缺失架构的选择。" #: platform/android/export/export.cpp -#, fuzzy msgid "Adding files..." -msgstr "æ£åœ¨æ·»åŠ %s..." +msgstr "æ£åœ¨æ·»åŠ æ–‡ä»¶â€¦â€¦" #: platform/android/export/export.cpp -#, fuzzy msgid "Could not export project files" -msgstr "æ— æ³•å†™å…¥æ–‡ä»¶:" +msgstr "æ— æ³•å¯¼å‡ºé¡¹ç›®æ–‡ä»¶" #: platform/android/export/export.cpp msgid "Aligning APK..." -msgstr "å¯¹é½ APK..." +msgstr "æ£åœ¨å¯¹é½ APK……" #: platform/android/export/export.cpp msgid "Could not unzip temporary unaligned APK." -msgstr "" +msgstr "æ— æ³•è§£åŽ‹æœªå¯¹é½çš„临时 APK。" #: platform/iphone/export/export.cpp platform/osx/export/export.cpp msgid "Identifier is missing." @@ -13368,45 +13225,40 @@ msgid "Could not write file:" msgstr "æ— æ³•å†™å…¥æ–‡ä»¶:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Could not read file:" -msgstr "æ— æ³•å†™å…¥æ–‡ä»¶:" +msgstr "æ— æ³•è¯»å–æ–‡ä»¶ï¼š" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Could not read HTML shell:" -msgstr "æ— æ³•è¯»å–自定义 HTML 壳层:" +msgstr "æ— æ³•è¯»å– HTML 壳:" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Could not create HTTP server directory:" -msgstr "æ— æ³•åˆ›å»ºæ–‡ä»¶å¤¹ã€‚" +msgstr "æ— æ³•åˆ›å»º HTTP æœåŠ¡å™¨ç›®å½•ï¼š" #: platform/javascript/export/export.cpp -#, fuzzy msgid "Error starting HTTP server:" -msgstr "ä¿å˜åœºæ™¯å‡ºé”™ã€‚" +msgstr "å¯åЍ HTTP æœåŠ¡å™¨æ—¶å‡ºé”™ï¼š" #: platform/osx/export/export.cpp -#, fuzzy msgid "Invalid bundle identifier:" -msgstr "æ— æ•ˆçš„æ ‡è¯†ç¬¦ï¼š" +msgstr "æ— æ•ˆçš„åŒ…æ ‡è¯†ç¬¦ï¼š" #: platform/osx/export/export.cpp msgid "Notarization: code signing required." -msgstr "" +msgstr "å…¬è¯ï¼šéœ€è¦ä»£ç ç¾å。" #: platform/osx/export/export.cpp msgid "Notarization: hardened runtime required." -msgstr "" +msgstr "å…¬è¯ï¼šéœ€è¦åŠ å¼ºçš„è¿è¡Œæ—¶çŽ¯å¢ƒã€‚" #: platform/osx/export/export.cpp msgid "Notarization: Apple ID name not specified." -msgstr "" +msgstr "å…¬è¯ï¼šæœªæŒ‡å®š Apple ID å称。" #: platform/osx/export/export.cpp msgid "Notarization: Apple ID password not specified." -msgstr "" +msgstr "å…¬è¯ï¼šæœªæŒ‡å®š Apple ID 密ç 。" #: platform/uwp/export/export.cpp msgid "Invalid package short name." @@ -13799,6 +13651,8 @@ msgid "" "longer has any effect.\n" "To remove this warning, disable the GIProbe's Compress property." msgstr "" +"å› ä¸ºå˜åœ¨å·²çŸ¥é—®é¢˜ï¼ŒGIProbe Compress 属性已被å¯ç”¨ï¼Œä¸ä¼šå†èµ·ä»»ä½•作用。\n" +"如果è¦ç§»é™¤æœ¬è¦å‘Šï¼Œè¯·ç¦ç”¨ GIProbe çš„ Compress 属性。" #: scene/3d/light.cpp msgid "A SpotLight with an angle wider than 90 degrees cannot cast shadows." @@ -13882,15 +13736,15 @@ msgstr "Node A 与 Node B 必须为ä¸åŒçš„ PhysicsBody" #: scene/3d/portal.cpp msgid "The RoomManager should not be a child or grandchild of a Portal." -msgstr "" +msgstr "RoomManager ä¸åº”该是 Portal çš„å节点或å™èŠ‚ç‚¹ã€‚" #: scene/3d/portal.cpp msgid "A Room should not be a child or grandchild of a Portal." -msgstr "" +msgstr "Room ä¸åº”该是 Portal çš„å节点或å™èŠ‚ç‚¹ã€‚" #: scene/3d/portal.cpp msgid "A RoomGroup should not be a child or grandchild of a Portal." -msgstr "" +msgstr "RoomGroup ä¸åº”该是 Portal çš„å节点或å™èŠ‚ç‚¹ã€‚" #: scene/3d/remote_transform.cpp msgid "" @@ -13901,42 +13755,82 @@ msgstr "" #: scene/3d/room.cpp msgid "A Room cannot have another Room as a child or grandchild." -msgstr "" +msgstr "Room ä¸èƒ½æ˜¯å¦ä¸€ä¸ª Room çš„å节点或å™èŠ‚ç‚¹ã€‚" #: scene/3d/room.cpp msgid "The RoomManager should not be placed inside a Room." -msgstr "" +msgstr "RoomManager ä¸åº”该被放置在 Room ä¸ã€‚" #: scene/3d/room.cpp msgid "A RoomGroup should not be placed inside a Room." -msgstr "" +msgstr "RoomGroup ä¸åº”该被放置在 Room ä¸ã€‚" #: scene/3d/room.cpp msgid "" "Room convex hull contains a large number of planes.\n" "Consider simplifying the room bound in order to increase performance." msgstr "" +"房间的凸包ä¸åŒ…å«å¤§é‡å¹³é¢ã€‚\n" +"为了æå‡æ€§èƒ½ï¼Œè¯·è€ƒè™‘简化房间的边界。" #: scene/3d/room_group.cpp msgid "The RoomManager should not be placed inside a RoomGroup." -msgstr "" +msgstr "RoomManager ä¸åº”该被放置在 RoomGroup ä¸ã€‚" #: scene/3d/room_manager.cpp msgid "The RoomList has not been assigned." -msgstr "" +msgstr "RoomList 尚未赋值。" #: scene/3d/room_manager.cpp msgid "The RoomList node should be a Spatial (or derived from Spatial)." -msgstr "" +msgstr "RoomList 节点应该是 Spatial(或者派生自 Spatial)。" #: scene/3d/room_manager.cpp msgid "" "Portal Depth Limit is set to Zero.\n" "Only the Room that the Camera is in will render." msgstr "" +"Portal Depth Limit 被设置为零。\n" +"ä»…ä¼šæ¸²æŸ“æ‘„åƒæœºæ‰€åœ¨æˆ¿é—´ã€‚" #: scene/3d/room_manager.cpp msgid "There should only be one RoomManager in the SceneTree." +msgstr "åœºæ™¯æ ‘ä¸ä»…能å˜åœ¨ä¸€ä¸ª RoomManager。" + +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." msgstr "" #: scene/3d/soft_body.cpp @@ -13999,7 +13893,7 @@ msgstr "没有动画: “%sâ€" #: scene/animation/animation_player.cpp msgid "Anim Apply Reset" -msgstr "" +msgstr "动画应用é‡ç½®" #: scene/animation/animation_tree.cpp msgid "In node '%s', invalid animation: '%s'." @@ -14165,25 +14059,24 @@ msgid "Invalid comparison function for that type." msgstr "è¯¥ç±»åž‹çš„æ¯”è¾ƒåŠŸèƒ½æ— æ•ˆã€‚" #: servers/visual/shader_language.cpp -#, fuzzy msgid "Varying may not be assigned in the '%s' function." -msgstr "å˜é‡åªèƒ½åœ¨é¡¶ç‚¹å‡½æ•°ä¸æŒ‡å®šã€‚" +msgstr "Varying ä¸èƒ½åœ¨â€œ%sâ€å‡½æ•°ä¸èµ‹å€¼ã€‚" #: servers/visual/shader_language.cpp msgid "" "Varyings which assigned in 'vertex' function may not be reassigned in " "'fragment' or 'light'." -msgstr "" +msgstr "已在“vertexâ€å‡½æ•°ä¸èµ‹å€¼çš„ varying ä¸èƒ½åœ¨â€œfragmentâ€æˆ–“lightâ€ä¸é‡æ–°èµ‹å€¼ã€‚" #: servers/visual/shader_language.cpp msgid "" "Varyings which assigned in 'fragment' function may not be reassigned in " "'vertex' or 'light'." -msgstr "" +msgstr "已在“fragmentâ€å‡½æ•°ä¸èµ‹å€¼çš„ varying ä¸èƒ½åœ¨â€œvertexâ€æˆ–“lightâ€ä¸é‡æ–°èµ‹å€¼ã€‚" #: servers/visual/shader_language.cpp msgid "Fragment-stage varying could not been accessed in custom function!" -msgstr "" +msgstr "ä¸èƒ½åœ¨è‡ªå®šä¹‰å‡½æ•°ä¸è®¿é—®ç‰‡æ®µ varyingï¼" #: servers/visual/shader_language.cpp msgid "Assignment to function." @@ -15184,9 +15077,6 @@ msgstr "ä¸å…许修改常é‡ã€‚" #~ msgid "I see..." #~ msgstr "好å§..." -#~ msgid "Can't open '%s'." -#~ msgstr "æ— æ³•æ‰“å¼€ \"%s\"。" - #~ msgid "Ugh" #~ msgstr "呃" diff --git a/editor/translations/zh_HK.po b/editor/translations/zh_HK.po index 9cae040eb4..e5327f79d9 100644 --- a/editor/translations/zh_HK.po +++ b/editor/translations/zh_HK.po @@ -361,6 +361,7 @@ msgstr "更改動畫循環" msgid "Remove Anim Track" msgstr "移除動畫軌跡" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp #, fuzzy msgid "Create NEW track for %s and insert key?" @@ -389,10 +390,28 @@ msgstr "新增" msgid "Anim Insert" msgstr "æ’入動畫" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "ä¸èƒ½é€£æŽ¥ã€‚" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "新增動畫" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "內容" + #: editor/animation_track_editor.cpp #, fuzzy msgid "Anim Create & Insert" @@ -990,7 +1009,7 @@ msgstr "新增" msgid "No results for \"%s\"." msgstr "" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2367,6 +2386,17 @@ msgid "New Window" msgstr "" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "" @@ -3209,10 +3239,6 @@ msgid "Save & Restart" msgstr "å„²å˜æª”案" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "" - -#: editor/editor_node.cpp #, fuzzy msgid "Update Continuously" msgstr "連續" @@ -3873,6 +3899,16 @@ msgid "Download from:" msgstr "下載出ç¾éŒ¯èª¤" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "ç€è¦½" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "載入錯誤" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8886,6 +8922,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "移除é¸é …" @@ -8916,6 +8958,12 @@ msgid "Remove All StyleBox Items" msgstr "移除é¸é …" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "åŠ åˆ°æœ€æ„›" @@ -12581,6 +12629,16 @@ msgstr "" msgid "Change Ray Shape Length" msgstr "" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "åªé™é¸ä¸" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "åªé™é¸ä¸" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "" @@ -14267,6 +14325,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "" @@ -14917,10 +15011,6 @@ msgstr "" #~ msgstr "多è¬!" #, fuzzy -#~ msgid "Can't open '%s'." -#~ msgstr "ä¸èƒ½é€£æŽ¥ã€‚" - -#, fuzzy #~ msgid "Ugh" #~ msgstr "å—¯......" diff --git a/editor/translations/zh_TW.po b/editor/translations/zh_TW.po index 2b4dd0fc03..4fc48abd03 100644 --- a/editor/translations/zh_TW.po +++ b/editor/translations/zh_TW.po @@ -360,6 +360,7 @@ msgstr "更改動畫循環模å¼" msgid "Remove Anim Track" msgstr "刪除動畫軌" +#. TRANSLATORS: %s will be replaced by a phrase describing the target of track. #: editor/animation_track_editor.cpp msgid "Create NEW track for %s and insert key?" msgstr "確定è¦ç‚º %s 建立動畫軌並æ’入關éµç•«æ ¼å—Žï¼Ÿ" @@ -384,10 +385,28 @@ msgstr "建立" msgid "Anim Insert" msgstr "æ’入動畫" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "node '%s'" +msgstr "無法開啟 \"%s\"。" + +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "animation" +msgstr "å‹•ç•«" + #: editor/animation_track_editor.cpp msgid "AnimationPlayer can't animate itself, only other players." msgstr "AnimationPlayer ä¸èƒ½æ’放自己,åªå¯æ’放其他 Player。" +#. TRANSLATORS: This describes the target of new animation track, will be inserted into another string. +#: editor/animation_track_editor.cpp +#, fuzzy +msgid "property '%s'" +msgstr "屬性「%sã€ä¸å˜åœ¨ã€‚" + #: editor/animation_track_editor.cpp msgid "Anim Create & Insert" msgstr "新增並æ’入動畫" @@ -955,7 +974,7 @@ msgstr "建立新的 %s" msgid "No results for \"%s\"." msgstr "找ä¸åˆ°èˆ‡ã€Œ%sã€ç›¸é—œçš„çµæžœã€‚" -#: editor/create_dialog.cpp +#: editor/create_dialog.cpp editor/property_selector.cpp msgid "No description available for %s." msgstr "" @@ -2292,6 +2311,17 @@ msgid "New Window" msgstr "新視窗" #: editor/editor_node.cpp +msgid "" +"Spins when the editor window redraws.\n" +"Update Continuously is enabled, which can increase power usage. Click to " +"disable it." +msgstr "" + +#: editor/editor_node.cpp +msgid "Spins when the editor window redraws." +msgstr "ç·¨è¼¯å™¨è¦–çª—é‡æ–°ç¹ªè£½æ™‚旋轉。" + +#: editor/editor_node.cpp msgid "Imported resources can't be saved." msgstr "匯入的資æºç„¡æ³•ä¿å˜ã€‚" @@ -3115,10 +3145,6 @@ msgid "Save & Restart" msgstr "ä¿å˜ä¸¦é‡æ–°å•Ÿå‹•" #: editor/editor_node.cpp -msgid "Spins when the editor window redraws." -msgstr "ç·¨è¼¯å™¨è¦–çª—é‡æ–°ç¹ªè£½æ™‚旋轉。" - -#: editor/editor_node.cpp msgid "Update Continuously" msgstr "æŒçºŒæ›´æ–°" @@ -3768,6 +3794,16 @@ msgid "Download from:" msgstr "下載錯誤" #: editor/export_template_manager.cpp +#, fuzzy +msgid "Open in Web Browser" +msgstr "在ç€è¦½å™¨ä¸åŸ·è¡Œ" + +#: editor/export_template_manager.cpp +#, fuzzy +msgid "Copy Mirror URL" +msgstr "複製錯誤" + +#: editor/export_template_manager.cpp msgid "Download and Install" msgstr "" @@ -8581,6 +8617,12 @@ msgid "" msgstr "" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"Select a theme type from the list to edit its items.\n" +"You can add a custom type or import a type with its items from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Remove All Color Items" msgstr "ç§»é™¤æ‰€æœ‰é …ç›®" @@ -8611,6 +8653,12 @@ msgid "Remove All StyleBox Items" msgstr "ç§»é™¤æ‰€æœ‰é …ç›®" #: editor/plugins/theme_editor_plugin.cpp +msgid "" +"This theme type is empty.\n" +"Add more items to it manually or by importing from another theme." +msgstr "" + +#: editor/plugins/theme_editor_plugin.cpp #, fuzzy msgid "Add Color Item" msgstr "æ–°å¢žé¡žåˆ¥é …ç›®" @@ -12194,6 +12242,16 @@ msgstr "更改圓柱形高度" msgid "Change Ray Shape Length" msgstr "更改射線形長度" +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Room Point Position" +msgstr "è¨å®šæ›²ç·šæŽ§åˆ¶é»žä½ç½®" + +#: editor/spatial_editor_gizmos.cpp +#, fuzzy +msgid "Set Portal Point Position" +msgstr "è¨å®šæ›²ç·šæŽ§åˆ¶é»žä½ç½®" + #: modules/csg/csg_gizmos.cpp msgid "Change Cylinder Radius" msgstr "更改圓柱體åŠå¾‘" @@ -13887,6 +13945,42 @@ msgstr "" msgid "There should only be one RoomManager in the SceneTree." msgstr "" +#: scene/3d/room_manager.cpp +msgid "" +"RoomList path is invalid.\n" +"Please check the RoomList branch has been assigned in the RoomManager." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "RoomList contains no Rooms, aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Misnamed nodes detected, check output log for details. Aborting." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "Portal link room not found, check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Portal autolink failed, check output log for details.\n" +"Check the portal is facing outwards from the source room." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Room overlap detected, cameras may work incorrectly in overlapping area.\n" +"Check output log for details." +msgstr "" + +#: scene/3d/room_manager.cpp +msgid "" +"Error calculating room bounds.\n" +"Ensure all rooms contain geometry or manual bounds." +msgstr "" + #: scene/3d/soft_body.cpp msgid "This body will be ignored until you set a mesh." msgstr "該形體在è¨å®šç¶²æ ¼å‰éƒ½å°‡è¢«å¿½ç•¥ã€‚" @@ -14806,9 +14900,6 @@ msgstr "ä¸å¯ä¿®æ”¹å¸¸æ•¸ã€‚" #~ msgid "Thanks!" #~ msgstr "è¬è¬!" -#~ msgid "Can't open '%s'." -#~ msgstr "無法開啟 \"%s\"。" - #~ msgid "Ugh" #~ msgstr "呃" diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp index 91465bcfea..bf11cc7f68 100644 --- a/modules/csg/csg_shape.cpp +++ b/modules/csg/csg_shape.cpp @@ -88,36 +88,40 @@ uint32_t CSGShape3D::get_collision_mask() const { return collision_mask; } -void CSGShape3D::set_collision_mask_bit(int p_bit, bool p_value) { - ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision mask bit must be between 0 and 31 inclusive."); - uint32_t mask = get_collision_mask(); +void CSGShape3D::set_collision_layer_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive."); + uint32_t collision_layer = get_collision_layer(); if (p_value) { - mask |= 1 << p_bit; + collision_layer |= 1 << (p_layer_number - 1); } else { - mask &= ~(1 << p_bit); + collision_layer &= ~(1 << (p_layer_number - 1)); } - set_collision_mask(mask); + set_collision_layer(collision_layer); } -bool CSGShape3D::get_collision_mask_bit(int p_bit) const { - ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision mask bit must be between 0 and 31 inclusive."); - return get_collision_mask() & (1 << p_bit); +bool CSGShape3D::get_collision_layer_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive."); + return get_collision_layer() & (1 << (p_layer_number - 1)); } -void CSGShape3D::set_collision_layer_bit(int p_bit, bool p_value) { - ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision layer bit must be between 0 and 31 inclusive."); - uint32_t layer = get_collision_layer(); +void CSGShape3D::set_collision_mask_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive."); + uint32_t mask = get_collision_mask(); if (p_value) { - layer |= 1 << p_bit; + mask |= 1 << (p_layer_number - 1); } else { - layer &= ~(1 << p_bit); + mask &= ~(1 << (p_layer_number - 1)); } - set_collision_layer(layer); + set_collision_mask(mask); } -bool CSGShape3D::get_collision_layer_bit(int p_bit) const { - ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision layer bit must be between 0 and 31 inclusive."); - return get_collision_layer() & (1 << p_bit); +bool CSGShape3D::get_collision_mask_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive."); + return get_collision_mask() & (1 << (p_layer_number - 1)); } bool CSGShape3D::is_root_shape() const { @@ -605,11 +609,11 @@ void CSGShape3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &CSGShape3D::set_collision_mask); ClassDB::bind_method(D_METHOD("get_collision_mask"), &CSGShape3D::get_collision_mask); - ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &CSGShape3D::set_collision_mask_bit); - ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &CSGShape3D::get_collision_mask_bit); + ClassDB::bind_method(D_METHOD("set_collision_mask_value", "layer_number", "value"), &CSGShape3D::set_collision_mask_value); + ClassDB::bind_method(D_METHOD("get_collision_mask_value", "layer_number"), &CSGShape3D::get_collision_mask_value); - ClassDB::bind_method(D_METHOD("set_collision_layer_bit", "bit", "value"), &CSGShape3D::set_collision_layer_bit); - ClassDB::bind_method(D_METHOD("get_collision_layer_bit", "bit"), &CSGShape3D::get_collision_layer_bit); + ClassDB::bind_method(D_METHOD("set_collision_layer_value", "layer_number", "value"), &CSGShape3D::set_collision_layer_value); + ClassDB::bind_method(D_METHOD("get_collision_layer_value", "layer_number"), &CSGShape3D::get_collision_layer_value); ClassDB::bind_method(D_METHOD("set_calculate_tangents", "enabled"), &CSGShape3D::set_calculate_tangents); ClassDB::bind_method(D_METHOD("is_calculating_tangents"), &CSGShape3D::is_calculating_tangents); diff --git a/modules/csg/csg_shape.h b/modules/csg/csg_shape.h index 5814ce4c83..5cf371665e 100644 --- a/modules/csg/csg_shape.h +++ b/modules/csg/csg_shape.h @@ -137,11 +137,11 @@ public: void set_collision_mask(uint32_t p_mask); uint32_t get_collision_mask() const; - void set_collision_layer_bit(int p_bit, bool p_value); - bool get_collision_layer_bit(int p_bit) const; + void set_collision_layer_value(int p_layer_number, bool p_value); + bool get_collision_layer_value(int p_layer_number) const; - void set_collision_mask_bit(int p_bit, bool p_value); - bool get_collision_mask_bit(int p_bit) const; + void set_collision_mask_value(int p_layer_number, bool p_value); + bool get_collision_mask_value(int p_layer_number) const; void set_snap(float p_snap); float get_snap() const; diff --git a/modules/csg/doc_classes/CSGShape3D.xml b/modules/csg/doc_classes/CSGShape3D.xml index f42ce8c379..446269f3f0 100644 --- a/modules/csg/doc_classes/CSGShape3D.xml +++ b/modules/csg/doc_classes/CSGShape3D.xml @@ -9,18 +9,18 @@ <tutorials> </tutorials> <methods> - <method name="get_collision_layer_bit" qualifiers="const"> + <method name="get_collision_layer_value" qualifiers="const"> <return type="bool" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <description> - Returns an individual bit on the collision mask. + Returns whether or not the specified layer of the [member collision_layer] is enabled, given a [code]layer_number[/code] between 1 and 32. </description> </method> - <method name="get_collision_mask_bit" qualifiers="const"> + <method name="get_collision_mask_value" qualifiers="const"> <return type="bool" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <description> - Returns an individual bit on the collision mask. + Returns whether or not the specified layer of the [member collision_mask] is enabled, given a [code]layer_number[/code] between 1 and 32. </description> </method> <method name="get_meshes" qualifiers="const"> @@ -35,20 +35,20 @@ Returns [code]true[/code] if this is a root shape and is thus the object that is rendered. </description> </method> - <method name="set_collision_layer_bit"> + <method name="set_collision_layer_value"> <return type="void" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <argument index="1" name="value" type="bool" /> <description> - Sets individual bits on the layer mask. Use this if you only need to change one layer's value. + Based on [code]value[/code], enables or disables the specified layer in the [member collision_layer], given a [code]layer_number[/code] between 1 and 32. </description> </method> - <method name="set_collision_mask_bit"> + <method name="set_collision_mask_value"> <return type="void" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <argument index="1" name="value" type="bool" /> <description> - Sets individual bits on the collision mask. Use this if you only need to change one layer's value. + Based on [code]value[/code], enables or disables the specified layer in the [member collision_mask], given a [code]layer_number[/code] between 1 and 32. </description> </method> </methods> diff --git a/modules/gridmap/doc_classes/GridMap.xml b/modules/gridmap/doc_classes/GridMap.xml index 8ea7384658..c1dbe63628 100644 --- a/modules/gridmap/doc_classes/GridMap.xml +++ b/modules/gridmap/doc_classes/GridMap.xml @@ -53,18 +53,18 @@ The orientation of the cell at the given grid coordinates. [code]-1[/code] is returned if the cell is empty. </description> </method> - <method name="get_collision_layer_bit" qualifiers="const"> + <method name="get_collision_layer_value" qualifiers="const"> <return type="bool" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <description> - Returns an individual bit on the [member collision_layer]. + Returns whether or not the specified layer of the [member collision_layer] is enabled, given a [code]layer_number[/code] between 1 and 32. </description> </method> - <method name="get_collision_mask_bit" qualifiers="const"> + <method name="get_collision_mask_value" qualifiers="const"> <return type="bool" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <description> - Returns an individual bit on the [member collision_mask]. + Returns whether or not the specified layer of the [member collision_mask] is enabled, given a [code]layer_number[/code] between 1 and 32. </description> </method> <method name="get_meshes"> @@ -119,20 +119,20 @@ <description> </description> </method> - <method name="set_collision_layer_bit"> + <method name="set_collision_layer_value"> <return type="void" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <argument index="1" name="value" type="bool" /> <description> - Sets an individual bit on the [member collision_layer]. + Based on [code]value[/code], enables or disables the specified layer in the [member collision_layer], given a [code]layer_number[/code] between 1 and 32. </description> </method> - <method name="set_collision_mask_bit"> + <method name="set_collision_mask_value"> <return type="void" /> - <argument index="0" name="bit" type="int" /> + <argument index="0" name="layer_number" type="int" /> <argument index="1" name="value" type="bool" /> <description> - Sets an individual bit on the [member collision_mask]. + Based on [code]value[/code], enables or disables the specified layer in the [member collision_mask], given a [code]layer_number[/code] between 1 and 32. </description> </method> <method name="world_to_map" qualifiers="const"> diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp index fea513c820..8e8b6f14ad 100644 --- a/modules/gridmap/grid_map.cpp +++ b/modules/gridmap/grid_map.cpp @@ -151,36 +151,40 @@ uint32_t GridMap::get_collision_mask() const { return collision_mask; } -void GridMap::set_collision_mask_bit(int p_bit, bool p_value) { - ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision mask bit must be between 0 and 31 inclusive."); - uint32_t mask = get_collision_mask(); +void GridMap::set_collision_layer_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive."); + uint32_t collision_layer = get_collision_layer(); if (p_value) { - mask |= 1 << p_bit; + collision_layer |= 1 << (p_layer_number - 1); } else { - mask &= ~(1 << p_bit); + collision_layer &= ~(1 << (p_layer_number - 1)); } - set_collision_mask(mask); + set_collision_layer(collision_layer); } -bool GridMap::get_collision_mask_bit(int p_bit) const { - ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision mask bit must be between 0 and 31 inclusive."); - return get_collision_mask() & (1 << p_bit); +bool GridMap::get_collision_layer_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive."); + return get_collision_layer() & (1 << (p_layer_number - 1)); } -void GridMap::set_collision_layer_bit(int p_bit, bool p_value) { - ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision layer bit must be between 0 and 31 inclusive."); - uint32_t layer = get_collision_layer(); +void GridMap::set_collision_mask_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive."); + uint32_t mask = get_collision_mask(); if (p_value) { - layer |= 1 << p_bit; + mask |= 1 << (p_layer_number - 1); } else { - layer &= ~(1 << p_bit); + mask &= ~(1 << (p_layer_number - 1)); } - set_collision_layer(layer); + set_collision_mask(mask); } -bool GridMap::get_collision_layer_bit(int p_bit) const { - ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision layer bit must be between 0 and 31 inclusive."); - return get_collision_layer() & (1 << p_bit); +bool GridMap::get_collision_mask_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive."); + return get_collision_mask() & (1 << (p_layer_number - 1)); } void GridMap::set_bake_navigation(bool p_bake_navigation) { @@ -794,11 +798,11 @@ void GridMap::_bind_methods() { ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &GridMap::set_collision_mask); ClassDB::bind_method(D_METHOD("get_collision_mask"), &GridMap::get_collision_mask); - ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &GridMap::set_collision_mask_bit); - ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &GridMap::get_collision_mask_bit); + ClassDB::bind_method(D_METHOD("set_collision_mask_value", "layer_number", "value"), &GridMap::set_collision_mask_value); + ClassDB::bind_method(D_METHOD("get_collision_mask_value", "layer_number"), &GridMap::get_collision_mask_value); - ClassDB::bind_method(D_METHOD("set_collision_layer_bit", "bit", "value"), &GridMap::set_collision_layer_bit); - ClassDB::bind_method(D_METHOD("get_collision_layer_bit", "bit"), &GridMap::get_collision_layer_bit); + ClassDB::bind_method(D_METHOD("set_collision_layer_value", "layer_number", "value"), &GridMap::set_collision_layer_value); + ClassDB::bind_method(D_METHOD("get_collision_layer_value", "layer_number"), &GridMap::get_collision_layer_value); ClassDB::bind_method(D_METHOD("set_bake_navigation", "bake_navigation"), &GridMap::set_bake_navigation); ClassDB::bind_method(D_METHOD("is_baking_navigation"), &GridMap::is_baking_navigation); diff --git a/modules/gridmap/grid_map.h b/modules/gridmap/grid_map.h index 8cd82e1f4c..879489fc70 100644 --- a/modules/gridmap/grid_map.h +++ b/modules/gridmap/grid_map.h @@ -217,11 +217,11 @@ public: void set_collision_mask(uint32_t p_mask); uint32_t get_collision_mask() const; - void set_collision_layer_bit(int p_bit, bool p_value); - bool get_collision_layer_bit(int p_bit) const; + void set_collision_layer_value(int p_layer_number, bool p_value); + bool get_collision_layer_value(int p_layer_number) const; - void set_collision_mask_bit(int p_bit, bool p_value); - bool get_collision_mask_bit(int p_bit) const; + void set_collision_mask_value(int p_layer_number, bool p_value); + bool get_collision_mask_value(int p_layer_number) const; void set_bake_navigation(bool p_bake_navigation); bool is_baking_navigation(); diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp index 1795bbe523..fc86abb6f1 100644 --- a/platform/android/export/export.cpp +++ b/platform/android/export/export.cpp @@ -30,3021 +30,7 @@ #include "export.h" -#include "core/config/project_settings.h" -#include "core/io/dir_access.h" -#include "core/io/file_access.h" -#include "core/io/image_loader.h" -#include "core/io/json.h" -#include "core/io/marshalls.h" -#include "core/io/zip_io.h" -#include "core/os/os.h" -#include "core/templates/safe_refcount.h" -#include "core/version.h" -#include "drivers/png/png_driver_common.h" -#include "editor/editor_export.h" -#include "editor/editor_log.h" -#include "editor/editor_node.h" -#include "editor/editor_settings.h" -#include "main/splash.gen.h" -#include "platform/android/export/gradle_export_util.h" -#include "platform/android/logo.gen.h" -#include "platform/android/plugin/godot_plugin_config.h" -#include "platform/android/run_icon.gen.h" - -#include <string.h> - -static const char *android_perms[] = { - "ACCESS_CHECKIN_PROPERTIES", - "ACCESS_COARSE_LOCATION", - "ACCESS_FINE_LOCATION", - "ACCESS_LOCATION_EXTRA_COMMANDS", - "ACCESS_MOCK_LOCATION", - "ACCESS_NETWORK_STATE", - "ACCESS_SURFACE_FLINGER", - "ACCESS_WIFI_STATE", - "ACCOUNT_MANAGER", - "ADD_VOICEMAIL", - "AUTHENTICATE_ACCOUNTS", - "BATTERY_STATS", - "BIND_ACCESSIBILITY_SERVICE", - "BIND_APPWIDGET", - "BIND_DEVICE_ADMIN", - "BIND_INPUT_METHOD", - "BIND_NFC_SERVICE", - "BIND_NOTIFICATION_LISTENER_SERVICE", - "BIND_PRINT_SERVICE", - "BIND_REMOTEVIEWS", - "BIND_TEXT_SERVICE", - "BIND_VPN_SERVICE", - "BIND_WALLPAPER", - "BLUETOOTH", - "BLUETOOTH_ADMIN", - "BLUETOOTH_PRIVILEGED", - "BRICK", - "BROADCAST_PACKAGE_REMOVED", - "BROADCAST_SMS", - "BROADCAST_STICKY", - "BROADCAST_WAP_PUSH", - "CALL_PHONE", - "CALL_PRIVILEGED", - "CAMERA", - "CAPTURE_AUDIO_OUTPUT", - "CAPTURE_SECURE_VIDEO_OUTPUT", - "CAPTURE_VIDEO_OUTPUT", - "CHANGE_COMPONENT_ENABLED_STATE", - "CHANGE_CONFIGURATION", - "CHANGE_NETWORK_STATE", - "CHANGE_WIFI_MULTICAST_STATE", - "CHANGE_WIFI_STATE", - "CLEAR_APP_CACHE", - "CLEAR_APP_USER_DATA", - "CONTROL_LOCATION_UPDATES", - "DELETE_CACHE_FILES", - "DELETE_PACKAGES", - "DEVICE_POWER", - "DIAGNOSTIC", - "DISABLE_KEYGUARD", - "DUMP", - "EXPAND_STATUS_BAR", - "FACTORY_TEST", - "FLASHLIGHT", - "FORCE_BACK", - "GET_ACCOUNTS", - "GET_PACKAGE_SIZE", - "GET_TASKS", - "GET_TOP_ACTIVITY_INFO", - "GLOBAL_SEARCH", - "HARDWARE_TEST", - "INJECT_EVENTS", - "INSTALL_LOCATION_PROVIDER", - "INSTALL_PACKAGES", - "INSTALL_SHORTCUT", - "INTERNAL_SYSTEM_WINDOW", - "INTERNET", - "KILL_BACKGROUND_PROCESSES", - "LOCATION_HARDWARE", - "MANAGE_ACCOUNTS", - "MANAGE_APP_TOKENS", - "MANAGE_DOCUMENTS", - "MASTER_CLEAR", - "MEDIA_CONTENT_CONTROL", - "MODIFY_AUDIO_SETTINGS", - "MODIFY_PHONE_STATE", - "MOUNT_FORMAT_FILESYSTEMS", - "MOUNT_UNMOUNT_FILESYSTEMS", - "NFC", - "PERSISTENT_ACTIVITY", - "PROCESS_OUTGOING_CALLS", - "READ_CALENDAR", - "READ_CALL_LOG", - "READ_CONTACTS", - "READ_EXTERNAL_STORAGE", - "READ_FRAME_BUFFER", - "READ_HISTORY_BOOKMARKS", - "READ_INPUT_STATE", - "READ_LOGS", - "READ_PHONE_STATE", - "READ_PROFILE", - "READ_SMS", - "READ_SOCIAL_STREAM", - "READ_SYNC_SETTINGS", - "READ_SYNC_STATS", - "READ_USER_DICTIONARY", - "REBOOT", - "RECEIVE_BOOT_COMPLETED", - "RECEIVE_MMS", - "RECEIVE_SMS", - "RECEIVE_WAP_PUSH", - "RECORD_AUDIO", - "REORDER_TASKS", - "RESTART_PACKAGES", - "SEND_RESPOND_VIA_MESSAGE", - "SEND_SMS", - "SET_ACTIVITY_WATCHER", - "SET_ALARM", - "SET_ALWAYS_FINISH", - "SET_ANIMATION_SCALE", - "SET_DEBUG_APP", - "SET_ORIENTATION", - "SET_POINTER_SPEED", - "SET_PREFERRED_APPLICATIONS", - "SET_PROCESS_LIMIT", - "SET_TIME", - "SET_TIME_ZONE", - "SET_WALLPAPER", - "SET_WALLPAPER_HINTS", - "SIGNAL_PERSISTENT_PROCESSES", - "STATUS_BAR", - "SUBSCRIBED_FEEDS_READ", - "SUBSCRIBED_FEEDS_WRITE", - "SYSTEM_ALERT_WINDOW", - "TRANSMIT_IR", - "UNINSTALL_SHORTCUT", - "UPDATE_DEVICE_STATS", - "USE_CREDENTIALS", - "USE_SIP", - "VIBRATE", - "WAKE_LOCK", - "WRITE_APN_SETTINGS", - "WRITE_CALENDAR", - "WRITE_CALL_LOG", - "WRITE_CONTACTS", - "WRITE_EXTERNAL_STORAGE", - "WRITE_GSERVICES", - "WRITE_HISTORY_BOOKMARKS", - "WRITE_PROFILE", - "WRITE_SECURE_SETTINGS", - "WRITE_SETTINGS", - "WRITE_SMS", - "WRITE_SOCIAL_STREAM", - "WRITE_SYNC_SETTINGS", - "WRITE_USER_DICTIONARY", - nullptr -}; - -static const char *SPLASH_IMAGE_EXPORT_PATH = "res/drawable-nodpi/splash.png"; -static const char *LEGACY_BUILD_SPLASH_IMAGE_EXPORT_PATH = "res/drawable-nodpi-v4/splash.png"; -static const char *SPLASH_BG_COLOR_PATH = "res/drawable-nodpi/splash_bg_color.png"; -static const char *LEGACY_BUILD_SPLASH_BG_COLOR_PATH = "res/drawable-nodpi-v4/splash_bg_color.png"; -static const char *SPLASH_CONFIG_PATH = "res://android/build/res/drawable/splash_drawable.xml"; -static const char *GDNATIVE_LIBS_PATH = "res://android/build/libs/gdnativelibs.json"; - -const String SPLASH_CONFIG_XML_CONTENT = R"SPLASH(<?xml version="1.0" encoding="utf-8"?> -<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:drawable="@drawable/splash_bg_color" /> - <item> - <bitmap - android:gravity="center" - android:filter="%s" - android:src="@drawable/splash" /> - </item> -</layer-list> -)SPLASH"; - -struct LauncherIcon { - const char *export_path; - int dimensions = 0; -}; - -static const int icon_densities_count = 6; -static const char *launcher_icon_option = "launcher_icons/main_192x192"; -static const char *launcher_adaptive_icon_foreground_option = "launcher_icons/adaptive_foreground_432x432"; -static const char *launcher_adaptive_icon_background_option = "launcher_icons/adaptive_background_432x432"; - -static const LauncherIcon launcher_icons[icon_densities_count] = { - { "res/mipmap-xxxhdpi-v4/icon.png", 192 }, - { "res/mipmap-xxhdpi-v4/icon.png", 144 }, - { "res/mipmap-xhdpi-v4/icon.png", 96 }, - { "res/mipmap-hdpi-v4/icon.png", 72 }, - { "res/mipmap-mdpi-v4/icon.png", 48 }, - { "res/mipmap/icon.png", 192 } -}; - -static const LauncherIcon launcher_adaptive_icon_foregrounds[icon_densities_count] = { - { "res/mipmap-xxxhdpi-v4/icon_foreground.png", 432 }, - { "res/mipmap-xxhdpi-v4/icon_foreground.png", 324 }, - { "res/mipmap-xhdpi-v4/icon_foreground.png", 216 }, - { "res/mipmap-hdpi-v4/icon_foreground.png", 162 }, - { "res/mipmap-mdpi-v4/icon_foreground.png", 108 }, - { "res/mipmap/icon_foreground.png", 432 } -}; - -static const LauncherIcon launcher_adaptive_icon_backgrounds[icon_densities_count] = { - { "res/mipmap-xxxhdpi-v4/icon_background.png", 432 }, - { "res/mipmap-xxhdpi-v4/icon_background.png", 324 }, - { "res/mipmap-xhdpi-v4/icon_background.png", 216 }, - { "res/mipmap-hdpi-v4/icon_background.png", 162 }, - { "res/mipmap-mdpi-v4/icon_background.png", 108 }, - { "res/mipmap/icon_background.png", 432 } -}; - -static const int EXPORT_FORMAT_APK = 0; -static const int EXPORT_FORMAT_AAB = 1; - -class EditorExportPlatformAndroid : public EditorExportPlatform { - GDCLASS(EditorExportPlatformAndroid, EditorExportPlatform); - - Ref<ImageTexture> logo; - Ref<ImageTexture> run_icon; - - struct Device { - String id; - String name; - String description; - int api_level = 0; - }; - - struct APKExportData { - zipFile apk; - EditorProgress *ep = nullptr; - }; - - struct CustomExportData { - bool debug; - Vector<String> libs; - }; - - Vector<PluginConfigAndroid> plugins; - String last_plugin_names; - uint64_t last_custom_build_time = 0; - SafeFlag plugins_changed; - Mutex plugins_lock; - Vector<Device> devices; - SafeFlag devices_changed; - Mutex device_lock; - Thread check_for_changes_thread; - SafeFlag quit_request; - - static void _check_for_changes_poll_thread(void *ud) { - EditorExportPlatformAndroid *ea = (EditorExportPlatformAndroid *)ud; - - while (!ea->quit_request.is_set()) { - // Check for plugins updates - { - // Nothing to do if we already know the plugins have changed. - if (!ea->plugins_changed.is_set()) { - Vector<PluginConfigAndroid> loaded_plugins = get_plugins(); - - MutexLock lock(ea->plugins_lock); - - if (ea->plugins.size() != loaded_plugins.size()) { - ea->plugins_changed.set(); - } else { - for (int i = 0; i < ea->plugins.size(); i++) { - if (ea->plugins[i].name != loaded_plugins[i].name) { - ea->plugins_changed.set(); - break; - } - } - } - - if (ea->plugins_changed.is_set()) { - ea->plugins = loaded_plugins; - } - } - } - - // Check for devices updates - String adb = get_adb_path(); - if (FileAccess::exists(adb)) { - String devices; - List<String> args; - args.push_back("devices"); - int ec; - OS::get_singleton()->execute(adb, args, &devices, &ec); - - Vector<String> ds = devices.split("\n"); - Vector<String> ldevices; - for (int i = 1; i < ds.size(); i++) { - String d = ds[i]; - int dpos = d.find("device"); - if (dpos == -1) { - continue; - } - d = d.substr(0, dpos).strip_edges(); - ldevices.push_back(d); - } - - MutexLock lock(ea->device_lock); - - bool different = false; - - if (ea->devices.size() != ldevices.size()) { - different = true; - } else { - for (int i = 0; i < ea->devices.size(); i++) { - if (ea->devices[i].id != ldevices[i]) { - different = true; - break; - } - } - } - - if (different) { - Vector<Device> ndevices; - - for (int i = 0; i < ldevices.size(); i++) { - Device d; - d.id = ldevices[i]; - for (int j = 0; j < ea->devices.size(); j++) { - if (ea->devices[j].id == ldevices[i]) { - d.description = ea->devices[j].description; - d.name = ea->devices[j].name; - d.api_level = ea->devices[j].api_level; - } - } - - if (d.description == "") { - //in the oven, request! - args.clear(); - args.push_back("-s"); - args.push_back(d.id); - args.push_back("shell"); - args.push_back("getprop"); - int ec2; - String dp; - - OS::get_singleton()->execute(adb, args, &dp, &ec2); - - Vector<String> props = dp.split("\n"); - String vendor; - String device; - d.description = "Device ID: " + d.id + "\n"; - d.api_level = 0; - for (int j = 0; j < props.size(); j++) { - // got information by `shell cat /system/build.prop` before and its format is "property=value" - // it's now changed to use `shell getporp` because of permission issue with Android 8.0 and above - // its format is "[property]: [value]" so changed it as like build.prop - String p = props[j]; - p = p.replace("]: ", "="); - p = p.replace("[", ""); - p = p.replace("]", ""); - - if (p.begins_with("ro.product.model=")) { - device = p.get_slice("=", 1).strip_edges(); - } else if (p.begins_with("ro.product.brand=")) { - vendor = p.get_slice("=", 1).strip_edges().capitalize(); - } else if (p.begins_with("ro.build.display.id=")) { - d.description += "Build: " + p.get_slice("=", 1).strip_edges() + "\n"; - } else if (p.begins_with("ro.build.version.release=")) { - d.description += "Release: " + p.get_slice("=", 1).strip_edges() + "\n"; - } else if (p.begins_with("ro.build.version.sdk=")) { - d.api_level = p.get_slice("=", 1).to_int(); - } else if (p.begins_with("ro.product.cpu.abi=")) { - d.description += "CPU: " + p.get_slice("=", 1).strip_edges() + "\n"; - } else if (p.begins_with("ro.product.manufacturer=")) { - d.description += "Manufacturer: " + p.get_slice("=", 1).strip_edges() + "\n"; - } else if (p.begins_with("ro.board.platform=")) { - d.description += "Chipset: " + p.get_slice("=", 1).strip_edges() + "\n"; - } else if (p.begins_with("ro.opengles.version=")) { - uint32_t opengl = p.get_slice("=", 1).to_int(); - d.description += "OpenGL: " + itos(opengl >> 16) + "." + itos((opengl >> 8) & 0xFF) + "." + itos((opengl)&0xFF) + "\n"; - } - } - - d.name = vendor + " " + device; - if (device == String()) { - continue; - } - } - - ndevices.push_back(d); - } - - ea->devices = ndevices; - ea->devices_changed.set(); - } - } - - uint64_t sleep = 200; - uint64_t wait = 3000000; - uint64_t time = OS::get_singleton()->get_ticks_usec(); - while (OS::get_singleton()->get_ticks_usec() - time < wait) { - OS::get_singleton()->delay_usec(1000 * sleep); - if (ea->quit_request.is_set()) { - break; - } - } - } - - if (EditorSettings::get_singleton()->get("export/android/shutdown_adb_on_exit")) { - String adb = get_adb_path(); - if (!FileAccess::exists(adb)) { - return; //adb not configured - } - - List<String> args; - args.push_back("kill-server"); - OS::get_singleton()->execute(adb, args); - }; - } - - String get_project_name(const String &p_name) const { - String aname; - if (p_name != "") { - aname = p_name; - } else { - aname = ProjectSettings::get_singleton()->get("application/config/name"); - } - - if (aname == "") { - aname = VERSION_NAME; - } - - return aname; - } - - String get_package_name(const String &p_package) const { - String pname = p_package; - String basename = ProjectSettings::get_singleton()->get("application/config/name"); - basename = basename.to_lower(); - - String name; - bool first = true; - for (int i = 0; i < basename.length(); i++) { - char32_t c = basename[i]; - if (c >= '0' && c <= '9' && first) { - continue; - } - if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) { - name += String::chr(c); - first = false; - } - } - if (name == "") { - name = "noname"; - } - - pname = pname.replace("$genname", name); - - return pname; - } - - bool is_package_name_valid(const String &p_package, String *r_error = nullptr) const { - String pname = p_package; - - if (pname.length() == 0) { - if (r_error) { - *r_error = TTR("Package name is missing."); - } - return false; - } - - int segments = 0; - bool first = true; - for (int i = 0; i < pname.length(); i++) { - char32_t c = pname[i]; - if (first && c == '.') { - if (r_error) { - *r_error = TTR("Package segments must be of non-zero length."); - } - return false; - } - if (c == '.') { - segments++; - first = true; - continue; - } - if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_')) { - if (r_error) { - *r_error = vformat(TTR("The character '%s' is not allowed in Android application package names."), String::chr(c)); - } - return false; - } - if (first && (c >= '0' && c <= '9')) { - if (r_error) { - *r_error = TTR("A digit cannot be the first character in a package segment."); - } - return false; - } - if (first && c == '_') { - if (r_error) { - *r_error = vformat(TTR("The character '%s' cannot be the first character in a package segment."), String::chr(c)); - } - return false; - } - first = false; - } - - if (segments == 0) { - if (r_error) { - *r_error = TTR("The package must have at least one '.' separator."); - } - return false; - } - - if (first) { - if (r_error) { - *r_error = TTR("Package segments must be of non-zero length."); - } - return false; - } - - return true; - } - - static bool _should_compress_asset(const String &p_path, const Vector<uint8_t> &p_data) { - /* - * By not compressing files with little or not benefit in doing so, - * a performance gain is expected attime. Moreover, if the APK is - * zip-aligned, assets stored as they are can be efficiently read by - * Android by memory-mapping them. - */ - - // -- Unconditional uncompress to mimic AAPT plus some other - - static const char *unconditional_compress_ext[] = { - // From https://github.com/android/platform_frameworks_base/blob/master/tools/aapt/Package.cpp - // These formats are already compressed, or don't compress well: - ".jpg", ".jpeg", ".png", ".gif", - ".wav", ".mp2", ".mp3", ".ogg", ".aac", - ".mpg", ".mpeg", ".mid", ".midi", ".smf", ".jet", - ".rtttl", ".imy", ".xmf", ".mp4", ".m4a", - ".m4v", ".3gp", ".3gpp", ".3g2", ".3gpp2", - ".amr", ".awb", ".wma", ".wmv", - // Godot-specific: - ".webp", // Same reasoning as .png - ".cfb", // Don't let small config files slow-down startup - ".scn", // Binary scenes are usually already compressed - ".stex", // Streamable textures are usually already compressed - // Trailer for easier processing - nullptr - }; - - for (const char **ext = unconditional_compress_ext; *ext; ++ext) { - if (p_path.to_lower().ends_with(String(*ext))) { - return false; - } - } - - // -- Compressed resource? - - if (p_data.size() >= 4 && p_data[0] == 'R' && p_data[1] == 'S' && p_data[2] == 'C' && p_data[3] == 'C') { - // Already compressed - return false; - } - - // --- TODO: Decide on texture resources according to their image compression setting - - return true; - } - - static zip_fileinfo get_zip_fileinfo() { - OS::Time time = OS::get_singleton()->get_time(); - OS::Date date = OS::get_singleton()->get_date(); - - zip_fileinfo zipfi; - zipfi.tmz_date.tm_hour = time.hour; - zipfi.tmz_date.tm_mday = date.day; - zipfi.tmz_date.tm_min = time.minute; - zipfi.tmz_date.tm_mon = date.month - 1; // tm_mon is zero indexed - zipfi.tmz_date.tm_sec = time.second; - zipfi.tmz_date.tm_year = date.year; - zipfi.dosDate = 0; - zipfi.external_fa = 0; - zipfi.internal_fa = 0; - - return zipfi; - } - - static Vector<String> get_abis() { - Vector<String> abis; - abis.push_back("armeabi-v7a"); - abis.push_back("arm64-v8a"); - abis.push_back("x86"); - abis.push_back("x86_64"); - return abis; - } - - /// List the gdap files in the directory specified by the p_path parameter. - static Vector<String> list_gdap_files(const String &p_path) { - Vector<String> dir_files; - DirAccessRef da = DirAccess::open(p_path); - if (da) { - da->list_dir_begin(); - while (true) { - String file = da->get_next(); - if (file == "") { - break; - } - - if (da->current_is_dir() || da->current_is_hidden()) { - continue; - } - - if (file.ends_with(PluginConfigAndroid::PLUGIN_CONFIG_EXT)) { - dir_files.push_back(file); - } - } - da->list_dir_end(); - } - - return dir_files; - } - - static Vector<PluginConfigAndroid> get_plugins() { - Vector<PluginConfigAndroid> loaded_plugins; - - String plugins_dir = ProjectSettings::get_singleton()->get_resource_path().plus_file("android/plugins"); - - // Add the prebuilt plugins - loaded_plugins.append_array(get_prebuilt_plugins(plugins_dir)); - - if (DirAccess::exists(plugins_dir)) { - Vector<String> plugins_filenames = list_gdap_files(plugins_dir); - - if (!plugins_filenames.is_empty()) { - Ref<ConfigFile> config_file = memnew(ConfigFile); - for (int i = 0; i < plugins_filenames.size(); i++) { - PluginConfigAndroid config = load_plugin_config(config_file, plugins_dir.plus_file(plugins_filenames[i])); - if (config.valid_config) { - loaded_plugins.push_back(config); - } else { - print_error("Invalid plugin config file " + plugins_filenames[i]); - } - } - } - } - - return loaded_plugins; - } - - static Vector<PluginConfigAndroid> get_enabled_plugins(const Ref<EditorExportPreset> &p_presets) { - Vector<PluginConfigAndroid> enabled_plugins; - Vector<PluginConfigAndroid> all_plugins = get_plugins(); - for (int i = 0; i < all_plugins.size(); i++) { - PluginConfigAndroid plugin = all_plugins[i]; - bool enabled = p_presets->get("plugins/" + plugin.name); - if (enabled) { - enabled_plugins.push_back(plugin); - } - } - - return enabled_plugins; - } - - static Error store_in_apk(APKExportData *ed, const String &p_path, const Vector<uint8_t> &p_data, int compression_method = Z_DEFLATED) { - zip_fileinfo zipfi = get_zip_fileinfo(); - zipOpenNewFileInZip(ed->apk, - p_path.utf8().get_data(), - &zipfi, - nullptr, - 0, - nullptr, - 0, - nullptr, - compression_method, - Z_DEFAULT_COMPRESSION); - - zipWriteInFileInZip(ed->apk, p_data.ptr(), p_data.size()); - zipCloseFileInZip(ed->apk); - - return OK; - } - - static Error save_apk_so(void *p_userdata, const SharedObject &p_so) { - if (!p_so.path.get_file().begins_with("lib")) { - String err = "Android .so file names must start with \"lib\", but got: " + p_so.path; - ERR_PRINT(err); - return FAILED; - } - APKExportData *ed = (APKExportData *)p_userdata; - Vector<String> abis = get_abis(); - bool exported = false; - for (int i = 0; i < p_so.tags.size(); ++i) { - // shared objects can be fat (compatible with multiple ABIs) - int abi_index = abis.find(p_so.tags[i]); - if (abi_index != -1) { - exported = true; - String abi = abis[abi_index]; - String dst_path = String("lib").plus_file(abi).plus_file(p_so.path.get_file()); - Vector<uint8_t> array = FileAccess::get_file_as_array(p_so.path); - Error store_err = store_in_apk(ed, dst_path, array); - ERR_FAIL_COND_V_MSG(store_err, store_err, "Cannot store in apk file '" + dst_path + "'."); - } - } - if (!exported) { - String abis_string = String(" ").join(abis); - String err = "Cannot determine ABI for library \"" + p_so.path + "\". One of the supported ABIs must be used as a tag: " + abis_string; - ERR_PRINT(err); - return FAILED; - } - return OK; - } - - static Error save_apk_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key) { - APKExportData *ed = (APKExportData *)p_userdata; - String dst_path = p_path.replace_first("res://", "assets/"); - - store_in_apk(ed, dst_path, p_data, _should_compress_asset(p_path, p_data) ? Z_DEFLATED : 0); - return OK; - } - - static Error ignore_apk_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key) { - return OK; - } - - static Error copy_gradle_so(void *p_userdata, const SharedObject &p_so) { - ERR_FAIL_COND_V_MSG(!p_so.path.get_file().begins_with("lib"), FAILED, - "Android .so file names must start with \"lib\", but got: " + p_so.path); - Vector<String> abis = get_abis(); - CustomExportData *export_data = (CustomExportData *)p_userdata; - bool exported = false; - for (int i = 0; i < p_so.tags.size(); ++i) { - int abi_index = abis.find(p_so.tags[i]); - if (abi_index != -1) { - exported = true; - String base = "res://android/build/libs"; - String type = export_data->debug ? "debug" : "release"; - String abi = abis[abi_index]; - String filename = p_so.path.get_file(); - String dst_path = base.plus_file(type).plus_file(abi).plus_file(filename); - Vector<uint8_t> data = FileAccess::get_file_as_array(p_so.path); - print_verbose("Copying .so file from " + p_so.path + " to " + dst_path); - Error err = store_file_at_path(dst_path, data); - ERR_FAIL_COND_V_MSG(err, err, "Failed to copy .so file from " + p_so.path + " to " + dst_path); - export_data->libs.push_back(dst_path); - } - } - ERR_FAIL_COND_V_MSG(!exported, FAILED, - "Cannot determine ABI for library \"" + p_so.path + "\". One of the supported ABIs must be used as a tag: " + String(" ").join(abis)); - return OK; - } - - void _get_permissions(const Ref<EditorExportPreset> &p_preset, bool p_give_internet, Vector<String> &r_permissions) { - const char **aperms = android_perms; - while (*aperms) { - bool enabled = p_preset->get("permissions/" + String(*aperms).to_lower()); - if (enabled) { - r_permissions.push_back("android.permission." + String(*aperms)); - } - aperms++; - } - PackedStringArray user_perms = p_preset->get("permissions/custom_permissions"); - for (int i = 0; i < user_perms.size(); i++) { - String user_perm = user_perms[i].strip_edges(); - if (!user_perm.is_empty()) { - r_permissions.push_back(user_perm); - } - } - if (p_give_internet) { - if (r_permissions.find("android.permission.INTERNET") == -1) { - r_permissions.push_back("android.permission.INTERNET"); - } - } - - int xr_mode_index = p_preset->get("xr_features/xr_mode"); - if (xr_mode_index == 1 /* XRMode.OVR */) { - int hand_tracking_index = p_preset->get("xr_features/hand_tracking"); // 0: none, 1: optional, 2: required - if (hand_tracking_index > 0) { - if (r_permissions.find("com.oculus.permission.HAND_TRACKING") == -1) { - r_permissions.push_back("com.oculus.permission.HAND_TRACKING"); - } - } - } - } - - void _write_tmp_manifest(const Ref<EditorExportPreset> &p_preset, bool p_give_internet, bool p_debug) { - print_verbose("Building temporary manifest.."); - String manifest_text = - "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" - "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" - " xmlns:tools=\"http://schemas.android.com/tools\">\n"; - - manifest_text += _get_screen_sizes_tag(p_preset); - manifest_text += _get_gles_tag(); - - Vector<String> perms; - _get_permissions(p_preset, p_give_internet, perms); - for (int i = 0; i < perms.size(); i++) { - manifest_text += vformat(" <uses-permission android:name=\"%s\" />\n", perms.get(i)); - } - - manifest_text += _get_xr_features_tag(p_preset); - manifest_text += _get_instrumentation_tag(p_preset); - manifest_text += _get_application_tag(p_preset); - manifest_text += "</manifest>\n"; - String manifest_path = vformat("res://android/build/src/%s/AndroidManifest.xml", (p_debug ? "debug" : "release")); - - print_verbose("Storing manifest into " + manifest_path + ": " + "\n" + manifest_text); - store_string_at_path(manifest_path, manifest_text); - } - - void _fix_manifest(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_manifest, bool p_give_internet) { - // Leaving the unused types commented because looking these constants up - // again later would be annoying - // const int CHUNK_AXML_FILE = 0x00080003; - // const int CHUNK_RESOURCEIDS = 0x00080180; - const int CHUNK_STRINGS = 0x001C0001; - // const int CHUNK_XML_END_NAMESPACE = 0x00100101; - const int CHUNK_XML_END_TAG = 0x00100103; - // const int CHUNK_XML_START_NAMESPACE = 0x00100100; - const int CHUNK_XML_START_TAG = 0x00100102; - // const int CHUNK_XML_TEXT = 0x00100104; - const int UTF8_FLAG = 0x00000100; - - Vector<String> string_table; - - uint32_t ofs = 8; - - uint32_t string_count = 0; - //uint32_t styles_count = 0; - uint32_t string_flags = 0; - uint32_t string_data_offset = 0; - - //uint32_t styles_offset = 0; - uint32_t string_table_begins = 0; - uint32_t string_table_ends = 0; - Vector<uint8_t> stable_extra; - - String version_name = p_preset->get("version/name"); - int version_code = p_preset->get("version/code"); - String package_name = p_preset->get("package/unique_name"); - - const int screen_orientation = - _get_android_orientation_value(DisplayServer::ScreenOrientation(int(GLOBAL_GET("display/window/handheld/orientation")))); - - bool screen_support_small = p_preset->get("screen/support_small"); - bool screen_support_normal = p_preset->get("screen/support_normal"); - bool screen_support_large = p_preset->get("screen/support_large"); - bool screen_support_xlarge = p_preset->get("screen/support_xlarge"); - - int xr_mode_index = p_preset->get("xr_features/xr_mode"); - - bool backup_allowed = p_preset->get("user_data_backup/allow"); - bool classify_as_game = p_preset->get("package/classify_as_game"); - - Vector<String> perms; - // Write permissions into the perms variable. - _get_permissions(p_preset, p_give_internet, perms); - - while (ofs < (uint32_t)p_manifest.size()) { - uint32_t chunk = decode_uint32(&p_manifest[ofs]); - uint32_t size = decode_uint32(&p_manifest[ofs + 4]); - - switch (chunk) { - case CHUNK_STRINGS: { - int iofs = ofs + 8; - - string_count = decode_uint32(&p_manifest[iofs]); - //styles_count = decode_uint32(&p_manifest[iofs + 4]); - string_flags = decode_uint32(&p_manifest[iofs + 8]); - string_data_offset = decode_uint32(&p_manifest[iofs + 12]); - //styles_offset = decode_uint32(&p_manifest[iofs + 16]); - /* - printf("string count: %i\n",string_count); - printf("flags: %i\n",string_flags); - printf("sdata ofs: %i\n",string_data_offset); - printf("styles ofs: %i\n",styles_offset); - */ - uint32_t st_offset = iofs + 20; - string_table.resize(string_count); - uint32_t string_end = 0; - - string_table_begins = st_offset; - - for (uint32_t i = 0; i < string_count; i++) { - uint32_t string_at = decode_uint32(&p_manifest[st_offset + i * 4]); - string_at += st_offset + string_count * 4; - - ERR_FAIL_COND_MSG(string_flags & UTF8_FLAG, "Unimplemented, can't read UTF-8 string table."); - - if (string_flags & UTF8_FLAG) { - } else { - uint32_t len = decode_uint16(&p_manifest[string_at]); - Vector<char32_t> ucstring; - ucstring.resize(len + 1); - for (uint32_t j = 0; j < len; j++) { - uint16_t c = decode_uint16(&p_manifest[string_at + 2 + 2 * j]); - ucstring.write[j] = c; - } - string_end = MAX(string_at + 2 + 2 * len, string_end); - ucstring.write[len] = 0; - string_table.write[i] = ucstring.ptr(); - } - } - - for (uint32_t i = string_end; i < (ofs + size); i++) { - stable_extra.push_back(p_manifest[i]); - } - - string_table_ends = ofs + size; - - } break; - case CHUNK_XML_START_TAG: { - int iofs = ofs + 8; - uint32_t name = decode_uint32(&p_manifest[iofs + 12]); - - String tname = string_table[name]; - uint32_t attrcount = decode_uint32(&p_manifest[iofs + 20]); - iofs += 28; - - for (uint32_t i = 0; i < attrcount; i++) { - uint32_t attr_nspace = decode_uint32(&p_manifest[iofs]); - uint32_t attr_name = decode_uint32(&p_manifest[iofs + 4]); - uint32_t attr_value = decode_uint32(&p_manifest[iofs + 8]); - uint32_t attr_resid = decode_uint32(&p_manifest[iofs + 16]); - - const String value = (attr_value != 0xFFFFFFFF) ? string_table[attr_value] : "Res #" + itos(attr_resid); - String attrname = string_table[attr_name]; - const String nspace = (attr_nspace != 0xFFFFFFFF) ? string_table[attr_nspace] : ""; - - //replace project information - if (tname == "manifest" && attrname == "package") { - string_table.write[attr_value] = get_package_name(package_name); - } - - if (tname == "manifest" && attrname == "versionCode") { - encode_uint32(version_code, &p_manifest.write[iofs + 16]); - } - - if (tname == "manifest" && attrname == "versionName") { - if (attr_value == 0xFFFFFFFF) { - WARN_PRINT("Version name in a resource, should be plain text"); - } else { - string_table.write[attr_value] = version_name; - } - } - - if (tname == "application" && attrname == "allowBackup") { - encode_uint32(backup_allowed, &p_manifest.write[iofs + 16]); - } - - if (tname == "application" && attrname == "isGame") { - encode_uint32(classify_as_game, &p_manifest.write[iofs + 16]); - } - - if (tname == "instrumentation" && attrname == "targetPackage") { - string_table.write[attr_value] = get_package_name(package_name); - } - - if (tname == "activity" && attrname == "screenOrientation") { - encode_uint32(screen_orientation, &p_manifest.write[iofs + 16]); - } - - if (tname == "supports-screens") { - if (attrname == "smallScreens") { - encode_uint32(screen_support_small ? 0xFFFFFFFF : 0, &p_manifest.write[iofs + 16]); - - } else if (attrname == "normalScreens") { - encode_uint32(screen_support_normal ? 0xFFFFFFFF : 0, &p_manifest.write[iofs + 16]); - - } else if (attrname == "largeScreens") { - encode_uint32(screen_support_large ? 0xFFFFFFFF : 0, &p_manifest.write[iofs + 16]); - - } else if (attrname == "xlargeScreens") { - encode_uint32(screen_support_xlarge ? 0xFFFFFFFF : 0, &p_manifest.write[iofs + 16]); - } - } - - iofs += 20; - } - - } break; - case CHUNK_XML_END_TAG: { - int iofs = ofs + 8; - uint32_t name = decode_uint32(&p_manifest[iofs + 12]); - String tname = string_table[name]; - - if (tname == "uses-feature") { - Vector<String> feature_names; - Vector<bool> feature_required_list; - Vector<int> feature_versions; - - if (xr_mode_index == 1 /* XRMode.OVR */) { - // Check for hand tracking - int hand_tracking_index = p_preset->get("xr_features/hand_tracking"); // 0: none, 1: optional, 2: required - if (hand_tracking_index > 0) { - feature_names.push_back("oculus.software.handtracking"); - feature_required_list.push_back(hand_tracking_index == 2); - feature_versions.push_back(-1); // no version attribute should be added. - } - } - - if (feature_names.size() > 0) { - ofs += 24; // skip over end tag - - // save manifest ending so we can restore it - Vector<uint8_t> manifest_end; - uint32_t manifest_cur_size = p_manifest.size(); - - manifest_end.resize(p_manifest.size() - ofs); - memcpy(manifest_end.ptrw(), &p_manifest[ofs], manifest_end.size()); - - int32_t attr_name_string = string_table.find("name"); - ERR_FAIL_COND_MSG(attr_name_string == -1, "Template does not have 'name' attribute."); - - int32_t ns_android_string = string_table.find("http://schemas.android.com/apk/res/android"); - if (ns_android_string == -1) { - string_table.push_back("http://schemas.android.com/apk/res/android"); - ns_android_string = string_table.size() - 1; - } - - int32_t attr_uses_feature_string = string_table.find("uses-feature"); - if (attr_uses_feature_string == -1) { - string_table.push_back("uses-feature"); - attr_uses_feature_string = string_table.size() - 1; - } - - int32_t attr_required_string = string_table.find("required"); - if (attr_required_string == -1) { - string_table.push_back("required"); - attr_required_string = string_table.size() - 1; - } - - for (int i = 0; i < feature_names.size(); i++) { - String feature_name = feature_names[i]; - bool feature_required = feature_required_list[i]; - int feature_version = feature_versions[i]; - bool has_version_attribute = feature_version != -1; - - print_line("Adding feature " + feature_name); - - int32_t feature_string = string_table.find(feature_name); - if (feature_string == -1) { - string_table.push_back(feature_name); - feature_string = string_table.size() - 1; - } - - String required_value_string = feature_required ? "true" : "false"; - int32_t required_value = string_table.find(required_value_string); - if (required_value == -1) { - string_table.push_back(required_value_string); - required_value = string_table.size() - 1; - } - - int32_t attr_version_string = -1; - int32_t version_value = -1; - int tag_size; - int attr_count; - if (has_version_attribute) { - attr_version_string = string_table.find("version"); - if (attr_version_string == -1) { - string_table.push_back("version"); - attr_version_string = string_table.size() - 1; - } - - version_value = string_table.find(itos(feature_version)); - if (version_value == -1) { - string_table.push_back(itos(feature_version)); - version_value = string_table.size() - 1; - } - - tag_size = 96; // node and three attrs + end node - attr_count = 3; - } else { - tag_size = 76; // node and two attrs + end node - attr_count = 2; - } - manifest_cur_size += tag_size + 24; - p_manifest.resize(manifest_cur_size); - - // start tag - encode_uint16(0x102, &p_manifest.write[ofs]); // type - encode_uint16(16, &p_manifest.write[ofs + 2]); // headersize - encode_uint32(tag_size, &p_manifest.write[ofs + 4]); // size - encode_uint32(0, &p_manifest.write[ofs + 8]); // lineno - encode_uint32(-1, &p_manifest.write[ofs + 12]); // comment - encode_uint32(-1, &p_manifest.write[ofs + 16]); // ns - encode_uint32(attr_uses_feature_string, &p_manifest.write[ofs + 20]); // name - encode_uint16(20, &p_manifest.write[ofs + 24]); // attr_start - encode_uint16(20, &p_manifest.write[ofs + 26]); // attr_size - encode_uint16(attr_count, &p_manifest.write[ofs + 28]); // num_attrs - encode_uint16(0, &p_manifest.write[ofs + 30]); // id_index - encode_uint16(0, &p_manifest.write[ofs + 32]); // class_index - encode_uint16(0, &p_manifest.write[ofs + 34]); // style_index - - // android:name attribute - encode_uint32(ns_android_string, &p_manifest.write[ofs + 36]); // ns - encode_uint32(attr_name_string, &p_manifest.write[ofs + 40]); // 'name' - encode_uint32(feature_string, &p_manifest.write[ofs + 44]); // raw_value - encode_uint16(8, &p_manifest.write[ofs + 48]); // typedvalue_size - p_manifest.write[ofs + 50] = 0; // typedvalue_always0 - p_manifest.write[ofs + 51] = 0x03; // typedvalue_type (string) - encode_uint32(feature_string, &p_manifest.write[ofs + 52]); // typedvalue reference - - // android:required attribute - encode_uint32(ns_android_string, &p_manifest.write[ofs + 56]); // ns - encode_uint32(attr_required_string, &p_manifest.write[ofs + 60]); // 'name' - encode_uint32(required_value, &p_manifest.write[ofs + 64]); // raw_value - encode_uint16(8, &p_manifest.write[ofs + 68]); // typedvalue_size - p_manifest.write[ofs + 70] = 0; // typedvalue_always0 - p_manifest.write[ofs + 71] = 0x03; // typedvalue_type (string) - encode_uint32(required_value, &p_manifest.write[ofs + 72]); // typedvalue reference - - ofs += 76; - - if (has_version_attribute) { - // android:version attribute - encode_uint32(ns_android_string, &p_manifest.write[ofs]); // ns - encode_uint32(attr_version_string, &p_manifest.write[ofs + 4]); // 'name' - encode_uint32(version_value, &p_manifest.write[ofs + 8]); // raw_value - encode_uint16(8, &p_manifest.write[ofs + 12]); // typedvalue_size - p_manifest.write[ofs + 14] = 0; // typedvalue_always0 - p_manifest.write[ofs + 15] = 0x03; // typedvalue_type (string) - encode_uint32(version_value, &p_manifest.write[ofs + 16]); // typedvalue reference - - ofs += 20; - } - - // end tag - encode_uint16(0x103, &p_manifest.write[ofs]); // type - encode_uint16(16, &p_manifest.write[ofs + 2]); // headersize - encode_uint32(24, &p_manifest.write[ofs + 4]); // size - encode_uint32(0, &p_manifest.write[ofs + 8]); // lineno - encode_uint32(-1, &p_manifest.write[ofs + 12]); // comment - encode_uint32(-1, &p_manifest.write[ofs + 16]); // ns - encode_uint32(attr_uses_feature_string, &p_manifest.write[ofs + 20]); // name - - ofs += 24; - } - memcpy(&p_manifest.write[ofs], manifest_end.ptr(), manifest_end.size()); - ofs -= 24; // go back over back end - } - } - if (tname == "manifest") { - // save manifest ending so we can restore it - Vector<uint8_t> manifest_end; - uint32_t manifest_cur_size = p_manifest.size(); - - manifest_end.resize(p_manifest.size() - ofs); - memcpy(manifest_end.ptrw(), &p_manifest[ofs], manifest_end.size()); - - int32_t attr_name_string = string_table.find("name"); - ERR_FAIL_COND_MSG(attr_name_string == -1, "Template does not have 'name' attribute."); - - int32_t ns_android_string = string_table.find("android"); - ERR_FAIL_COND_MSG(ns_android_string == -1, "Template does not have 'android' namespace."); - - int32_t attr_uses_permission_string = string_table.find("uses-permission"); - if (attr_uses_permission_string == -1) { - string_table.push_back("uses-permission"); - attr_uses_permission_string = string_table.size() - 1; - } - - for (int i = 0; i < perms.size(); ++i) { - print_line("Adding permission " + perms[i]); - - manifest_cur_size += 56 + 24; // node + end node - p_manifest.resize(manifest_cur_size); - - // Add permission to the string pool - int32_t perm_string = string_table.find(perms[i]); - if (perm_string == -1) { - string_table.push_back(perms[i]); - perm_string = string_table.size() - 1; - } - - // start tag - encode_uint16(0x102, &p_manifest.write[ofs]); // type - encode_uint16(16, &p_manifest.write[ofs + 2]); // headersize - encode_uint32(56, &p_manifest.write[ofs + 4]); // size - encode_uint32(0, &p_manifest.write[ofs + 8]); // lineno - encode_uint32(-1, &p_manifest.write[ofs + 12]); // comment - encode_uint32(-1, &p_manifest.write[ofs + 16]); // ns - encode_uint32(attr_uses_permission_string, &p_manifest.write[ofs + 20]); // name - encode_uint16(20, &p_manifest.write[ofs + 24]); // attr_start - encode_uint16(20, &p_manifest.write[ofs + 26]); // attr_size - encode_uint16(1, &p_manifest.write[ofs + 28]); // num_attrs - encode_uint16(0, &p_manifest.write[ofs + 30]); // id_index - encode_uint16(0, &p_manifest.write[ofs + 32]); // class_index - encode_uint16(0, &p_manifest.write[ofs + 34]); // style_index - - // attribute - encode_uint32(ns_android_string, &p_manifest.write[ofs + 36]); // ns - encode_uint32(attr_name_string, &p_manifest.write[ofs + 40]); // 'name' - encode_uint32(perm_string, &p_manifest.write[ofs + 44]); // raw_value - encode_uint16(8, &p_manifest.write[ofs + 48]); // typedvalue_size - p_manifest.write[ofs + 50] = 0; // typedvalue_always0 - p_manifest.write[ofs + 51] = 0x03; // typedvalue_type (string) - encode_uint32(perm_string, &p_manifest.write[ofs + 52]); // typedvalue reference - - ofs += 56; - - // end tag - encode_uint16(0x103, &p_manifest.write[ofs]); // type - encode_uint16(16, &p_manifest.write[ofs + 2]); // headersize - encode_uint32(24, &p_manifest.write[ofs + 4]); // size - encode_uint32(0, &p_manifest.write[ofs + 8]); // lineno - encode_uint32(-1, &p_manifest.write[ofs + 12]); // comment - encode_uint32(-1, &p_manifest.write[ofs + 16]); // ns - encode_uint32(attr_uses_permission_string, &p_manifest.write[ofs + 20]); // name - - ofs += 24; - } - - // copy footer back in - memcpy(&p_manifest.write[ofs], manifest_end.ptr(), manifest_end.size()); - } - } break; - } - - ofs += size; - } - - //create new andriodmanifest binary - - Vector<uint8_t> ret; - ret.resize(string_table_begins + string_table.size() * 4); - - for (uint32_t i = 0; i < string_table_begins; i++) { - ret.write[i] = p_manifest[i]; - } - - ofs = 0; - for (int i = 0; i < string_table.size(); i++) { - encode_uint32(ofs, &ret.write[string_table_begins + i * 4]); - ofs += string_table[i].length() * 2 + 2 + 2; - } - - ret.resize(ret.size() + ofs); - string_data_offset = ret.size() - ofs; - uint8_t *chars = &ret.write[string_data_offset]; - for (int i = 0; i < string_table.size(); i++) { - String s = string_table[i]; - encode_uint16(s.length(), chars); - chars += 2; - for (int j = 0; j < s.length(); j++) { - encode_uint16(s[j], chars); - chars += 2; - } - encode_uint16(0, chars); - chars += 2; - } - - for (int i = 0; i < stable_extra.size(); i++) { - ret.push_back(stable_extra[i]); - } - - //pad - while (ret.size() % 4) { - ret.push_back(0); - } - - uint32_t new_stable_end = ret.size(); - - uint32_t extra = (p_manifest.size() - string_table_ends); - ret.resize(new_stable_end + extra); - for (uint32_t i = 0; i < extra; i++) { - ret.write[new_stable_end + i] = p_manifest[string_table_ends + i]; - } - - while (ret.size() % 4) { - ret.push_back(0); - } - encode_uint32(ret.size(), &ret.write[4]); //update new file size - - encode_uint32(new_stable_end - 8, &ret.write[12]); //update new string table size - encode_uint32(string_table.size(), &ret.write[16]); //update new number of strings - encode_uint32(string_data_offset - 8, &ret.write[28]); //update new string data offset - - p_manifest = ret; - } - - static String _parse_string(const uint8_t *p_bytes, bool p_utf8) { - uint32_t offset = 0; - uint32_t len = 0; - - if (p_utf8) { - uint8_t byte = p_bytes[offset]; - if (byte & 0x80) { - offset += 2; - } else { - offset += 1; - } - byte = p_bytes[offset]; - offset++; - if (byte & 0x80) { - len = byte & 0x7F; - len = (len << 8) + p_bytes[offset]; - offset++; - } else { - len = byte; - } - } else { - len = decode_uint16(&p_bytes[offset]); - offset += 2; - if (len & 0x8000) { - len &= 0x7FFF; - len = (len << 16) + decode_uint16(&p_bytes[offset]); - offset += 2; - } - } - - if (p_utf8) { - Vector<uint8_t> str8; - str8.resize(len + 1); - for (uint32_t i = 0; i < len; i++) { - str8.write[i] = p_bytes[offset + i]; - } - str8.write[len] = 0; - String str; - str.parse_utf8((const char *)str8.ptr()); - return str; - } else { - String str; - for (uint32_t i = 0; i < len; i++) { - char32_t c = decode_uint16(&p_bytes[offset + i * 2]); - if (c == 0) { - break; - } - str += String::chr(c); - } - return str; - } - } - - void _fix_resources(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &r_manifest) { - const int UTF8_FLAG = 0x00000100; - - uint32_t string_block_len = decode_uint32(&r_manifest[16]); - uint32_t string_count = decode_uint32(&r_manifest[20]); - uint32_t string_flags = decode_uint32(&r_manifest[28]); - const uint32_t string_table_begins = 40; - - Vector<String> string_table; - - String package_name = p_preset->get("package/name"); - - for (uint32_t i = 0; i < string_count; i++) { - uint32_t offset = decode_uint32(&r_manifest[string_table_begins + i * 4]); - offset += string_table_begins + string_count * 4; - - String str = _parse_string(&r_manifest[offset], string_flags & UTF8_FLAG); - - if (str.begins_with("godot-project-name")) { - if (str == "godot-project-name") { - //project name - str = get_project_name(package_name); - - } else { - String lang = str.substr(str.rfind("-") + 1, str.length()).replace("-", "_"); - String prop = "application/config/name_" + lang; - if (ProjectSettings::get_singleton()->has_setting(prop)) { - str = ProjectSettings::get_singleton()->get(prop); - } else { - str = get_project_name(package_name); - } - } - } - - string_table.push_back(str); - } - - //write a new string table, but use 16 bits - Vector<uint8_t> ret; - ret.resize(string_table_begins + string_table.size() * 4); - - for (uint32_t i = 0; i < string_table_begins; i++) { - ret.write[i] = r_manifest[i]; - } - - int ofs = 0; - for (int i = 0; i < string_table.size(); i++) { - encode_uint32(ofs, &ret.write[string_table_begins + i * 4]); - ofs += string_table[i].length() * 2 + 2 + 2; - } - - ret.resize(ret.size() + ofs); - uint8_t *chars = &ret.write[ret.size() - ofs]; - for (int i = 0; i < string_table.size(); i++) { - String s = string_table[i]; - encode_uint16(s.length(), chars); - chars += 2; - for (int j = 0; j < s.length(); j++) { - encode_uint16(s[j], chars); - chars += 2; - } - encode_uint16(0, chars); - chars += 2; - } - - //pad - while (ret.size() % 4) { - ret.push_back(0); - } - - //change flags to not use utf8 - encode_uint32(string_flags & ~0x100, &ret.write[28]); - //change length - encode_uint32(ret.size() - 12, &ret.write[16]); - //append the rest... - int rest_from = 12 + string_block_len; - int rest_to = ret.size(); - int rest_len = (r_manifest.size() - rest_from); - ret.resize(ret.size() + (r_manifest.size() - rest_from)); - for (int i = 0; i < rest_len; i++) { - ret.write[rest_to + i] = r_manifest[rest_from + i]; - } - //finally update the size - encode_uint32(ret.size(), &ret.write[4]); - - r_manifest = ret; - //printf("end\n"); - } - - void _load_image_data(const Ref<Image> &p_splash_image, Vector<uint8_t> &p_data) { - Vector<uint8_t> png_buffer; - Error err = PNGDriverCommon::image_to_png(p_splash_image, png_buffer); - if (err == OK) { - p_data.resize(png_buffer.size()); - memcpy(p_data.ptrw(), png_buffer.ptr(), p_data.size()); - } else { - String err_str = String("Failed to convert splash image to png."); - WARN_PRINT(err_str.utf8().get_data()); - } - } - - void _process_launcher_icons(const String &p_file_name, const Ref<Image> &p_source_image, int dimension, Vector<uint8_t> &p_data) { - Ref<Image> working_image = p_source_image; - - if (p_source_image->get_width() != dimension || p_source_image->get_height() != dimension) { - working_image = p_source_image->duplicate(); - working_image->resize(dimension, dimension, Image::Interpolation::INTERPOLATE_LANCZOS); - } - - Vector<uint8_t> png_buffer; - Error err = PNGDriverCommon::image_to_png(working_image, png_buffer); - if (err == OK) { - p_data.resize(png_buffer.size()); - memcpy(p_data.ptrw(), png_buffer.ptr(), p_data.size()); - } else { - String err_str = String("Failed to convert resized icon (") + p_file_name + ") to png."; - WARN_PRINT(err_str.utf8().get_data()); - } - } - - String load_splash_refs(Ref<Image> &splash_image, Ref<Image> &splash_bg_color_image) { - bool scale_splash = ProjectSettings::get_singleton()->get("application/boot_splash/fullsize"); - bool apply_filter = ProjectSettings::get_singleton()->get("application/boot_splash/use_filter"); - String project_splash_path = ProjectSettings::get_singleton()->get("application/boot_splash/image"); - - if (!project_splash_path.is_empty()) { - splash_image.instantiate(); - print_verbose("Loading splash image: " + project_splash_path); - const Error err = ImageLoader::load_image(project_splash_path, splash_image); - if (err) { - if (OS::get_singleton()->is_stdout_verbose()) { - print_error("- unable to load splash image from " + project_splash_path + " (" + itos(err) + ")"); - } - splash_image.unref(); - } - } - - if (splash_image.is_null()) { - // Use the default - print_verbose("Using default splash image."); - splash_image = Ref<Image>(memnew(Image(boot_splash_png))); - } - - if (scale_splash) { - Size2 screen_size = Size2(ProjectSettings::get_singleton()->get("display/window/size/width"), ProjectSettings::get_singleton()->get("display/window/size/height")); - int width, height; - if (screen_size.width > screen_size.height) { - // scale horizontally - height = screen_size.height; - width = splash_image->get_width() * screen_size.height / splash_image->get_height(); - } else { - // scale vertically - width = screen_size.width; - height = splash_image->get_height() * screen_size.width / splash_image->get_width(); - } - splash_image->resize(width, height); - } - - // Setup the splash bg color - bool bg_color_valid; - Color bg_color = ProjectSettings::get_singleton()->get("application/boot_splash/bg_color", &bg_color_valid); - if (!bg_color_valid) { - bg_color = boot_splash_bg_color; - } - - print_verbose("Creating splash background color image."); - splash_bg_color_image.instantiate(); - splash_bg_color_image->create(splash_image->get_width(), splash_image->get_height(), false, splash_image->get_format()); - splash_bg_color_image->fill(bg_color); - - String processed_splash_config_xml = vformat(SPLASH_CONFIG_XML_CONTENT, bool_to_string(apply_filter)); - return processed_splash_config_xml; - } - - void load_icon_refs(const Ref<EditorExportPreset> &p_preset, Ref<Image> &icon, Ref<Image> &foreground, Ref<Image> &background) { - String project_icon_path = ProjectSettings::get_singleton()->get("application/config/icon"); - - icon.instantiate(); - foreground.instantiate(); - background.instantiate(); - - // Regular icon: user selection -> project icon -> default. - String path = static_cast<String>(p_preset->get(launcher_icon_option)).strip_edges(); - print_verbose("Loading regular icon from " + path); - if (path.is_empty() || ImageLoader::load_image(path, icon) != OK) { - print_verbose("- falling back to project icon: " + project_icon_path); - ImageLoader::load_image(project_icon_path, icon); - } - - // Adaptive foreground: user selection -> regular icon (user selection -> project icon -> default). - path = static_cast<String>(p_preset->get(launcher_adaptive_icon_foreground_option)).strip_edges(); - print_verbose("Loading adaptive foreground icon from " + path); - if (path.is_empty() || ImageLoader::load_image(path, foreground) != OK) { - print_verbose("- falling back to using the regular icon"); - foreground = icon; - } - - // Adaptive background: user selection -> default. - path = static_cast<String>(p_preset->get(launcher_adaptive_icon_background_option)).strip_edges(); - if (!path.is_empty()) { - print_verbose("Loading adaptive background icon from " + path); - ImageLoader::load_image(path, background); - } - } - - void store_image(const LauncherIcon launcher_icon, const Vector<uint8_t> &data) { - store_image(launcher_icon.export_path, data); - } - - void store_image(const String &export_path, const Vector<uint8_t> &data) { - String img_path = export_path.insert(0, "res://android/build/"); - store_file_at_path(img_path, data); - } - - void _copy_icons_to_gradle_project(const Ref<EditorExportPreset> &p_preset, - const String &processed_splash_config_xml, - const Ref<Image> &splash_image, - const Ref<Image> &splash_bg_color_image, - const Ref<Image> &main_image, - const Ref<Image> &foreground, - const Ref<Image> &background) { - // Store the splash configuration - if (!processed_splash_config_xml.is_empty()) { - print_verbose("Storing processed splash configuration: " + String("\n") + processed_splash_config_xml); - store_string_at_path(SPLASH_CONFIG_PATH, processed_splash_config_xml); - } - - // Store the splash image - if (splash_image.is_valid() && !splash_image->is_empty()) { - print_verbose("Storing splash image in " + String(SPLASH_IMAGE_EXPORT_PATH)); - Vector<uint8_t> data; - _load_image_data(splash_image, data); - store_image(SPLASH_IMAGE_EXPORT_PATH, data); - } - - // Store the splash bg color image - if (splash_bg_color_image.is_valid() && !splash_bg_color_image->is_empty()) { - print_verbose("Storing splash background image in " + String(SPLASH_BG_COLOR_PATH)); - Vector<uint8_t> data; - _load_image_data(splash_bg_color_image, data); - store_image(SPLASH_BG_COLOR_PATH, data); - } - - // Prepare images to be resized for the icons. If some image ends up being uninitialized, - // the default image from the export template will be used. - - for (int i = 0; i < icon_densities_count; ++i) { - if (main_image.is_valid() && !main_image->is_empty()) { - print_verbose("Processing launcher icon for dimension " + itos(launcher_icons[i].dimensions) + " into " + launcher_icons[i].export_path); - Vector<uint8_t> data; - _process_launcher_icons(launcher_icons[i].export_path, main_image, launcher_icons[i].dimensions, data); - store_image(launcher_icons[i], data); - } - - if (foreground.is_valid() && !foreground->is_empty()) { - print_verbose("Processing launcher adaptive icon foreground for dimension " + itos(launcher_adaptive_icon_foregrounds[i].dimensions) + " into " + launcher_adaptive_icon_foregrounds[i].export_path); - Vector<uint8_t> data; - _process_launcher_icons(launcher_adaptive_icon_foregrounds[i].export_path, foreground, - launcher_adaptive_icon_foregrounds[i].dimensions, data); - store_image(launcher_adaptive_icon_foregrounds[i], data); - } - - if (background.is_valid() && !background->is_empty()) { - print_verbose("Processing launcher adaptive icon background for dimension " + itos(launcher_adaptive_icon_backgrounds[i].dimensions) + " into " + launcher_adaptive_icon_backgrounds[i].export_path); - Vector<uint8_t> data; - _process_launcher_icons(launcher_adaptive_icon_backgrounds[i].export_path, background, - launcher_adaptive_icon_backgrounds[i].dimensions, data); - store_image(launcher_adaptive_icon_backgrounds[i], data); - } - } - } - - static Vector<String> get_enabled_abis(const Ref<EditorExportPreset> &p_preset) { - Vector<String> abis = get_abis(); - Vector<String> enabled_abis; - for (int i = 0; i < abis.size(); ++i) { - bool is_enabled = p_preset->get("architectures/" + abis[i]); - if (is_enabled) { - enabled_abis.push_back(abis[i]); - } - } - return enabled_abis; - } - -public: - typedef Error (*EditorExportSaveFunction)(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key); - -public: - virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) override { - String driver = ProjectSettings::get_singleton()->get("rendering/driver/driver_name"); - if (driver == "GLES2") { - r_features->push_back("etc"); - } - // FIXME: Review what texture formats are used for Vulkan. - if (driver == "Vulkan") { - r_features->push_back("etc2"); - } - - Vector<String> abis = get_enabled_abis(p_preset); - for (int i = 0; i < abis.size(); ++i) { - r_features->push_back(abis[i]); - } - } - - virtual void get_export_options(List<ExportOption> *r_options) override { - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.apk"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.apk"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "custom_template/use_custom_build"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "custom_template/export_format", PROPERTY_HINT_ENUM, "Export APK,Export AAB"), EXPORT_FORMAT_APK)); - - Vector<PluginConfigAndroid> plugins_configs = get_plugins(); - for (int i = 0; i < plugins_configs.size(); i++) { - print_verbose("Found Android plugin " + plugins_configs[i].name); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "plugins/" + plugins_configs[i].name), false)); - } - plugins_changed.clear(); - - Vector<String> abis = get_abis(); - for (int i = 0; i < abis.size(); ++i) { - String abi = abis[i]; - bool is_default = (abi == "armeabi-v7a" || abi == "arm64-v8a"); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "architectures/" + abi), is_default)); - } - - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug", PROPERTY_HINT_GLOBAL_FILE, "*.keystore,*.jks"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug_user"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug_password"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release", PROPERTY_HINT_GLOBAL_FILE, "*.keystore,*.jks"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_user"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_password"), "")); - - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "one_click_deploy/clear_previous_install"), false)); - - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/code", PROPERTY_HINT_RANGE, "1,4096,1,or_greater"), 1)); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "version/name"), "1.0")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/unique_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "ext.domain.name"), "org.godotengine.$genname")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name [default if blank]"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/signed"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/classify_as_game"), true)); - - 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::BOOL, "graphics/32_bits_framebuffer"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "graphics/opengl_debug"), false)); - - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/xr_mode", PROPERTY_HINT_ENUM, "Regular,Oculus Mobile VR"), 0)); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/hand_tracking", PROPERTY_HINT_ENUM, "None,Optional,Required"), 0)); - - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/immersive_mode"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_small"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_normal"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_large"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_xlarge"), true)); - - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "user_data_backup/allow"), false)); - - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "command_line/extra_args"), "")); - - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "apk_expansion/enable"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "apk_expansion/SALT"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "apk_expansion/public_key", PROPERTY_HINT_MULTILINE_TEXT), "")); - - r_options->push_back(ExportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "permissions/custom_permissions"), PackedStringArray())); - - const char **perms = android_perms; - while (*perms) { - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "permissions/" + String(*perms).to_lower()), false)); - perms++; - } - } - - virtual String get_name() const override { - return "Android"; - } - - virtual String get_os_name() const override { - return "Android"; - } - - virtual Ref<Texture2D> get_logo() const override { - return logo; - } - - virtual bool should_update_export_options() override { - bool export_options_changed = plugins_changed.is_set(); - if (export_options_changed) { - // don't clear unless we're reporting true, to avoid race - plugins_changed.clear(); - } - return export_options_changed; - } - - virtual bool poll_export() override { - bool dc = devices_changed.is_set(); - if (dc) { - // don't clear unless we're reporting true, to avoid race - devices_changed.clear(); - } - return dc; - } - - virtual int get_options_count() const override { - MutexLock lock(device_lock); - return devices.size(); - } - - virtual String get_options_tooltip() const override { - return TTR("Select device from the list"); - } - - virtual String get_option_label(int p_index) const override { - ERR_FAIL_INDEX_V(p_index, devices.size(), ""); - MutexLock lock(device_lock); - return devices[p_index].name; - } - - virtual String get_option_tooltip(int p_index) const override { - ERR_FAIL_INDEX_V(p_index, devices.size(), ""); - MutexLock lock(device_lock); - String s = devices[p_index].description; - if (devices.size() == 1) { - // Tooltip will be: - // Name - // Description - s = devices[p_index].name + "\n\n" + s; - } - return s; - } - - virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) override { - ERR_FAIL_INDEX_V(p_device, devices.size(), ERR_INVALID_PARAMETER); - - String can_export_error; - bool can_export_missing_templates; - if (!can_export(p_preset, can_export_error, can_export_missing_templates)) { - EditorNode::add_io_error(can_export_error); - return ERR_UNCONFIGURED; - } - - MutexLock lock(device_lock); - - EditorProgress ep("run", vformat(TTR("Running on %s"), devices[p_device].name), 3); - - String adb = get_adb_path(); - - // Export_temp APK. - if (ep.step(TTR("Exporting APK..."), 0)) { - return ERR_SKIP; - } - - const bool use_remote = (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) || (p_debug_flags & DEBUG_FLAG_DUMB_CLIENT); - const bool use_reverse = devices[p_device].api_level >= 21; - - if (use_reverse) { - p_debug_flags |= DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST; - } - - String tmp_export_path = EditorPaths::get_singleton()->get_cache_dir().plus_file("tmpexport." + uitos(OS::get_singleton()->get_unix_time()) + ".apk"); - -#define CLEANUP_AND_RETURN(m_err) \ - { \ - DirAccess::remove_file_or_error(tmp_export_path); \ - return m_err; \ - } - - // Export to temporary APK before sending to device. - Error err = export_project_helper(p_preset, true, tmp_export_path, EXPORT_FORMAT_APK, true, p_debug_flags); - - if (err != OK) { - CLEANUP_AND_RETURN(err); - } - - List<String> args; - int rv; - String output; - - bool remove_prev = p_preset->get("one_click_deploy/clear_previous_install"); - String version_name = p_preset->get("version/name"); - String package_name = p_preset->get("package/unique_name"); - - if (remove_prev) { - if (ep.step(TTR("Uninstalling..."), 1)) { - CLEANUP_AND_RETURN(ERR_SKIP); - } - - print_line("Uninstalling previous version: " + devices[p_device].name); - - args.push_back("-s"); - args.push_back(devices[p_device].id); - args.push_back("uninstall"); - args.push_back(get_package_name(package_name)); - - output.clear(); - err = OS::get_singleton()->execute(adb, args, &output, &rv, true); - print_verbose(output); - } - - print_line("Installing to device (please wait...): " + devices[p_device].name); - if (ep.step(TTR("Installing to device, please wait..."), 2)) { - CLEANUP_AND_RETURN(ERR_SKIP); - } - - args.clear(); - args.push_back("-s"); - args.push_back(devices[p_device].id); - args.push_back("install"); - args.push_back("-r"); - args.push_back(tmp_export_path); - - output.clear(); - err = OS::get_singleton()->execute(adb, args, &output, &rv, true); - print_verbose(output); - if (err || rv != 0) { - EditorNode::add_io_error(vformat(TTR("Could not install to device: %s"), output)); - CLEANUP_AND_RETURN(ERR_CANT_CREATE); - } - - if (use_remote) { - if (use_reverse) { - static const char *const msg = "--- Device API >= 21; debugging over USB ---"; - EditorNode::get_singleton()->get_log()->add_message(msg, EditorLog::MSG_TYPE_EDITOR); - print_line(String(msg).to_upper()); - - args.clear(); - args.push_back("-s"); - args.push_back(devices[p_device].id); - args.push_back("reverse"); - args.push_back("--remove-all"); - output.clear(); - OS::get_singleton()->execute(adb, args, &output, &rv, true); - print_verbose(output); - - if (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) { - int dbg_port = EditorSettings::get_singleton()->get("network/debug/remote_port"); - args.clear(); - args.push_back("-s"); - args.push_back(devices[p_device].id); - args.push_back("reverse"); - args.push_back("tcp:" + itos(dbg_port)); - args.push_back("tcp:" + itos(dbg_port)); - - output.clear(); - OS::get_singleton()->execute(adb, args, &output, &rv, true); - print_verbose(output); - print_line("Reverse result: " + itos(rv)); - } - - if (p_debug_flags & DEBUG_FLAG_DUMB_CLIENT) { - int fs_port = EditorSettings::get_singleton()->get("filesystem/file_server/port"); - - args.clear(); - args.push_back("-s"); - args.push_back(devices[p_device].id); - args.push_back("reverse"); - args.push_back("tcp:" + itos(fs_port)); - args.push_back("tcp:" + itos(fs_port)); - - output.clear(); - err = OS::get_singleton()->execute(adb, args, &output, &rv, true); - print_verbose(output); - print_line("Reverse result2: " + itos(rv)); - } - } else { - static const char *const msg = "--- Device API < 21; debugging over Wi-Fi ---"; - EditorNode::get_singleton()->get_log()->add_message(msg, EditorLog::MSG_TYPE_EDITOR); - print_line(String(msg).to_upper()); - } - } - - if (ep.step(TTR("Running on device..."), 3)) { - CLEANUP_AND_RETURN(ERR_SKIP); - } - args.clear(); - args.push_back("-s"); - args.push_back(devices[p_device].id); - args.push_back("shell"); - args.push_back("am"); - args.push_back("start"); - if ((bool)EditorSettings::get_singleton()->get("export/android/force_system_user") && devices[p_device].api_level >= 17) { // Multi-user introduced in Android 17 - args.push_back("--user"); - args.push_back("0"); - } - args.push_back("-a"); - args.push_back("android.intent.action.MAIN"); - args.push_back("-n"); - args.push_back(get_package_name(package_name) + "/com.godot.game.GodotApp"); - - output.clear(); - err = OS::get_singleton()->execute(adb, args, &output, &rv, true); - print_verbose(output); - if (err || rv != 0) { - EditorNode::add_io_error(TTR("Could not execute on device.")); - CLEANUP_AND_RETURN(ERR_CANT_CREATE); - } - - CLEANUP_AND_RETURN(OK); -#undef CLEANUP_AND_RETURN - } - - virtual Ref<Texture2D> get_run_icon() const override { - return run_icon; - } - - static String get_adb_path() { - String exe_ext = ""; - if (OS::get_singleton()->get_name() == "Windows") { - exe_ext = ".exe"; - } - String sdk_path = EditorSettings::get_singleton()->get("export/android/android_sdk_path"); - return sdk_path.plus_file("platform-tools/adb" + exe_ext); - } - - static String get_apksigner_path() { - String exe_ext = ""; - if (OS::get_singleton()->get_name() == "Windows") { - exe_ext = ".bat"; - } - String apksigner_command_name = "apksigner" + exe_ext; - String sdk_path = EditorSettings::get_singleton()->get("export/android/android_sdk_path"); - String apksigner_path = ""; - - Error errn; - String build_tools_dir = sdk_path.plus_file("build-tools"); - DirAccessRef da = DirAccess::open(build_tools_dir, &errn); - if (errn != OK) { - print_error("Unable to open Android 'build-tools' directory."); - return apksigner_path; - } - - // There are additional versions directories we need to go through. - da->list_dir_begin(); - String sub_dir = da->get_next(); - while (!sub_dir.is_empty()) { - if (!sub_dir.begins_with(".") && da->current_is_dir()) { - // Check if the tool is here. - String tool_path = build_tools_dir.plus_file(sub_dir).plus_file(apksigner_command_name); - if (FileAccess::exists(tool_path)) { - apksigner_path = tool_path; - break; - } - } - sub_dir = da->get_next(); - } - da->list_dir_end(); - - if (apksigner_path.is_empty()) { - EditorNode::get_singleton()->show_warning(TTR("Unable to find the 'apksigner' tool.")); - } - - return apksigner_path; - } - - virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override { - String err; - bool valid = false; - - // Look for export templates (first official, and if defined custom templates). - - if (!bool(p_preset->get("custom_template/use_custom_build"))) { - String template_err; - bool dvalid = false; - bool rvalid = false; - bool has_export_templates = false; - - if (p_preset->get("custom_template/debug") != "") { - dvalid = FileAccess::exists(p_preset->get("custom_template/debug")); - if (!dvalid) { - template_err += TTR("Custom debug template not found.") + "\n"; - } - } else { - has_export_templates |= exists_export_template("android_debug.apk", &template_err); - } - - if (p_preset->get("custom_template/release") != "") { - rvalid = FileAccess::exists(p_preset->get("custom_template/release")); - if (!rvalid) { - template_err += TTR("Custom release template not found.") + "\n"; - } - } else { - has_export_templates |= exists_export_template("android_release.apk", &template_err); - } - - r_missing_templates = !has_export_templates; - valid = dvalid || rvalid || has_export_templates; - if (!valid) { - err += template_err; - } - } else { - bool installed_android_build_template = FileAccess::exists("res://android/build/build.gradle"); - if (!installed_android_build_template) { - r_missing_templates = !exists_export_template("android_source.zip", &err); - err += TTR("Android build template not installed in the project. Install it from the Project menu.") + "\n"; - } else { - r_missing_templates = false; - } - - valid = installed_android_build_template && !r_missing_templates; - } - - // Validate the rest of the configuration. - - String dk = p_preset->get("keystore/debug"); - String dk_user = p_preset->get("keystore/debug_user"); - String dk_password = p_preset->get("keystore/debug_password"); - - if ((dk.is_empty() || dk_user.is_empty() || dk_password.is_empty()) && (!dk.is_empty() || !dk_user.is_empty() || !dk_password.is_empty())) { - valid = false; - err += TTR("Either Debug Keystore, Debug User AND Debug Password settings must be configured OR none of them.") + "\n"; - } - - if (!FileAccess::exists(dk)) { - dk = EditorSettings::get_singleton()->get("export/android/debug_keystore"); - if (!FileAccess::exists(dk)) { - valid = false; - err += TTR("Debug keystore not configured in the Editor Settings nor in the preset.") + "\n"; - } - } - - String rk = p_preset->get("keystore/release"); - String rk_user = p_preset->get("keystore/release_user"); - String rk_password = p_preset->get("keystore/release_password"); - - if ((rk.is_empty() || rk_user.is_empty() || rk_password.is_empty()) && (!rk.is_empty() || !rk_user.is_empty() || !rk_password.is_empty())) { - valid = false; - err += TTR("Either Release Keystore, Release User AND Release Password settings must be configured OR none of them.") + "\n"; - } - - if (!rk.is_empty() && !FileAccess::exists(rk)) { - valid = false; - err += TTR("Release keystore incorrectly configured in the export preset.") + "\n"; - } - - String sdk_path = EditorSettings::get_singleton()->get("export/android/android_sdk_path"); - if (sdk_path == "") { - err += TTR("A valid Android SDK path is required in Editor Settings.") + "\n"; - valid = false; - } else { - Error errn; - // Check for the platform-tools directory. - DirAccessRef da = DirAccess::open(sdk_path.plus_file("platform-tools"), &errn); - if (errn != OK) { - err += TTR("Invalid Android SDK path in Editor Settings."); - err += TTR("Missing 'platform-tools' directory!"); - err += "\n"; - valid = false; - } - - // Validate that adb is available - String adb_path = get_adb_path(); - if (!FileAccess::exists(adb_path)) { - err += TTR("Unable to find Android SDK platform-tools' adb command."); - err += TTR("Please check in the Android SDK directory specified in Editor Settings."); - err += "\n"; - valid = false; - } - - // Check for the build-tools directory. - DirAccessRef build_tools_da = DirAccess::open(sdk_path.plus_file("build-tools"), &errn); - if (errn != OK) { - err += TTR("Invalid Android SDK path in Editor Settings."); - err += TTR("Missing 'build-tools' directory!"); - err += "\n"; - valid = false; - } - - // Validate that apksigner is available - String apksigner_path = get_apksigner_path(); - if (!FileAccess::exists(apksigner_path)) { - err += TTR("Unable to find Android SDK build-tools' apksigner command."); - err += TTR("Please check in the Android SDK directory specified in Editor Settings."); - err += "\n"; - valid = false; - } - } - - bool apk_expansion = p_preset->get("apk_expansion/enable"); - - if (apk_expansion) { - String apk_expansion_pkey = p_preset->get("apk_expansion/public_key"); - - if (apk_expansion_pkey == "") { - valid = false; - - err += TTR("Invalid public key for APK expansion.") + "\n"; - } - } - - String pn = p_preset->get("package/unique_name"); - String pn_err; - - if (!is_package_name_valid(get_package_name(pn), &pn_err)) { - valid = false; - err += TTR("Invalid package name:") + " " + pn_err + "\n"; - } - - String etc_error = test_etc2(); - if (etc_error != String()) { - valid = false; - err += etc_error; - } - - // Ensure that `Use Custom Build` is enabled if a plugin is selected. - String enabled_plugins_names = get_plugins_names(get_enabled_plugins(p_preset)); - bool custom_build_enabled = p_preset->get("custom_template/use_custom_build"); - if (!enabled_plugins_names.is_empty() && !custom_build_enabled) { - valid = false; - err += TTR("\"Use Custom Build\" must be enabled to use the plugins."); - err += "\n"; - } - - // Validate the Xr features are properly populated - int xr_mode_index = p_preset->get("xr_features/xr_mode"); - int hand_tracking = p_preset->get("xr_features/hand_tracking"); - if (xr_mode_index != /* XRMode.OVR*/ 1) { - if (hand_tracking > 0) { - valid = false; - err += TTR("\"Hand Tracking\" is only valid when \"Xr Mode\" is \"Oculus Mobile VR\"."); - err += "\n"; - } - } - - if (int(p_preset->get("custom_template/export_format")) == EXPORT_FORMAT_AAB && - !bool(p_preset->get("custom_template/use_custom_build"))) { - valid = false; - err += TTR("\"Export AAB\" is only valid when \"Use Custom Build\" is enabled."); - err += "\n"; - } - - r_error = err; - return valid; - } - - virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override { - List<String> list; - list.push_back("apk"); - list.push_back("aab"); - return list; - } - - inline bool is_clean_build_required(Vector<PluginConfigAndroid> enabled_plugins) { - String plugin_names = get_plugins_names(enabled_plugins); - bool first_build = last_custom_build_time == 0; - bool have_plugins_changed = false; - - if (!first_build) { - have_plugins_changed = plugin_names != last_plugin_names; - if (!have_plugins_changed) { - for (int i = 0; i < enabled_plugins.size(); i++) { - if (enabled_plugins.get(i).last_updated > last_custom_build_time) { - have_plugins_changed = true; - break; - } - } - } - } - - last_custom_build_time = OS::get_singleton()->get_unix_time(); - last_plugin_names = plugin_names; - - return have_plugins_changed || first_build; - } - - String get_apk_expansion_fullpath(const Ref<EditorExportPreset> &p_preset, const String &p_path) { - int version_code = p_preset->get("version/code"); - String package_name = p_preset->get("package/unique_name"); - String apk_file_name = "main." + itos(version_code) + "." + get_package_name(package_name) + ".obb"; - String fullpath = p_path.get_base_dir().plus_file(apk_file_name); - return fullpath; - } - - Error save_apk_expansion_file(const Ref<EditorExportPreset> &p_preset, const String &p_path) { - String fullpath = get_apk_expansion_fullpath(p_preset, p_path); - Error err = save_pack(p_preset, fullpath); - return err; - } - - void get_command_line_flags(const Ref<EditorExportPreset> &p_preset, const String &p_path, int p_flags, Vector<uint8_t> &r_command_line_flags) { - String cmdline = p_preset->get("command_line/extra_args"); - Vector<String> command_line_strings = cmdline.strip_edges().split(" "); - for (int i = 0; i < command_line_strings.size(); i++) { - if (command_line_strings[i].strip_edges().length() == 0) { - command_line_strings.remove(i); - i--; - } - } - - gen_export_flags(command_line_strings, p_flags); - - bool apk_expansion = p_preset->get("apk_expansion/enable"); - if (apk_expansion) { - String fullpath = get_apk_expansion_fullpath(p_preset, p_path); - String apk_expansion_public_key = p_preset->get("apk_expansion/public_key"); - - command_line_strings.push_back("--use_apk_expansion"); - command_line_strings.push_back("--apk_expansion_md5"); - command_line_strings.push_back(FileAccess::get_md5(fullpath)); - command_line_strings.push_back("--apk_expansion_key"); - command_line_strings.push_back(apk_expansion_public_key.strip_edges()); - } - - int xr_mode_index = p_preset->get("xr_features/xr_mode"); - if (xr_mode_index == 1) { - command_line_strings.push_back("--xr_mode_ovr"); - } else { // XRMode.REGULAR is the default. - command_line_strings.push_back("--xr_mode_regular"); - } - - bool use_32_bit_framebuffer = p_preset->get("graphics/32_bits_framebuffer"); - if (use_32_bit_framebuffer) { - command_line_strings.push_back("--use_depth_32"); - } - - bool immersive = p_preset->get("screen/immersive_mode"); - if (immersive) { - command_line_strings.push_back("--use_immersive"); - } - - bool debug_opengl = p_preset->get("graphics/opengl_debug"); - if (debug_opengl) { - command_line_strings.push_back("--debug_opengl"); - } - - if (command_line_strings.size()) { - r_command_line_flags.resize(4); - encode_uint32(command_line_strings.size(), &r_command_line_flags.write[0]); - for (int i = 0; i < command_line_strings.size(); i++) { - print_line(itos(i) + " param: " + command_line_strings[i]); - CharString command_line_argument = command_line_strings[i].utf8(); - int base = r_command_line_flags.size(); - int length = command_line_argument.length(); - if (length == 0) { - continue; - } - r_command_line_flags.resize(base + 4 + length); - encode_uint32(length, &r_command_line_flags.write[base]); - memcpy(&r_command_line_flags.write[base + 4], command_line_argument.ptr(), length); - } - } - } - - Error sign_apk(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &export_path, EditorProgress &ep) { - int export_format = int(p_preset->get("custom_template/export_format")); - String export_label = export_format == EXPORT_FORMAT_AAB ? "AAB" : "APK"; - String release_keystore = p_preset->get("keystore/release"); - String release_username = p_preset->get("keystore/release_user"); - String release_password = p_preset->get("keystore/release_password"); - - String apksigner = get_apksigner_path(); - print_verbose("Starting signing of the " + export_label + " binary using " + apksigner); - if (!FileAccess::exists(apksigner)) { - EditorNode::add_io_error(vformat(TTR("'apksigner' could not be found.\nPlease check the command is available in the Android SDK build-tools directory.\nThe resulting %s is unsigned."), export_label)); - return OK; - } - - String keystore; - String password; - String user; - if (p_debug) { - keystore = p_preset->get("keystore/debug"); - password = p_preset->get("keystore/debug_password"); - user = p_preset->get("keystore/debug_user"); - - if (keystore.is_empty()) { - keystore = EditorSettings::get_singleton()->get("export/android/debug_keystore"); - password = EditorSettings::get_singleton()->get("export/android/debug_keystore_pass"); - user = EditorSettings::get_singleton()->get("export/android/debug_keystore_user"); - } - - if (ep.step(vformat(TTR("Signing debug %s..."), export_label), 104)) { - return ERR_SKIP; - } - - } else { - keystore = release_keystore; - password = release_password; - user = release_username; - - if (ep.step(vformat(TTR("Signing release %s..."), export_label), 104)) { - return ERR_SKIP; - } - } - - if (!FileAccess::exists(keystore)) { - EditorNode::add_io_error(TTR("Could not find keystore, unable to export.")); - return ERR_FILE_CANT_OPEN; - } - - String output; - List<String> args; - args.push_back("sign"); - args.push_back("--verbose"); - args.push_back("--ks"); - args.push_back(keystore); - args.push_back("--ks-pass"); - args.push_back("pass:" + password); - args.push_back("--ks-key-alias"); - args.push_back(user); - args.push_back(export_path); - if (p_debug) { - // We only print verbose logs for debug builds to avoid leaking release keystore credentials. - print_verbose("Signing debug binary using: " + String("\n") + apksigner + " " + join_list(args, String(" "))); - } - int retval; - output.clear(); - OS::get_singleton()->execute(apksigner, args, &output, &retval, true); - print_verbose(output); - if (retval) { - EditorNode::add_io_error(vformat(TTR("'apksigner' returned with error #%d"), retval)); - return ERR_CANT_CREATE; - } - - if (ep.step(vformat(TTR("Verifying %s..."), export_label), 105)) { - return ERR_SKIP; - } - - args.clear(); - args.push_back("verify"); - args.push_back("--verbose"); - args.push_back(export_path); - if (p_debug) { - print_verbose("Verifying signed build using: " + String("\n") + apksigner + " " + join_list(args, String(" "))); - } - - output.clear(); - OS::get_singleton()->execute(apksigner, args, &output, &retval, true); - print_verbose(output); - if (retval) { - EditorNode::add_io_error(vformat(TTR("'apksigner' verification of %s failed."), export_label)); - return ERR_CANT_CREATE; - } - - print_verbose("Successfully completed signing build."); - return OK; - } - - void _clear_assets_directory() { - DirAccessRef da_res = DirAccess::create(DirAccess::ACCESS_RESOURCES); - if (da_res->dir_exists("res://android/build/assets")) { - print_verbose("Clearing assets directory.."); - DirAccessRef da_assets = DirAccess::open("res://android/build/assets"); - da_assets->erase_contents_recursive(); - da_res->remove("res://android/build/assets"); - } - } - - void _remove_copied_libs() { - print_verbose("Removing previously installed libraries..."); - Error error; - String libs_json = FileAccess::get_file_as_string(GDNATIVE_LIBS_PATH, &error); - if (error || libs_json.is_empty()) { - print_verbose("No previously installed libraries found"); - return; - } - - JSON json; - error = json.parse(libs_json); - ERR_FAIL_COND_MSG(error, "Error parsing \"" + libs_json + "\" on line " + itos(json.get_error_line()) + ": " + json.get_error_message()); - - Vector<String> libs = json.get_data(); - DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES); - for (int i = 0; i < libs.size(); i++) { - print_verbose("Removing previously installed library " + libs[i]); - da->remove(libs[i]); - } - da->remove(GDNATIVE_LIBS_PATH); - } - - String join_list(List<String> parts, const String &separator) const { - String ret; - for (int i = 0; i < parts.size(); ++i) { - if (i > 0) { - ret += separator; - } - ret += parts[i]; - } - return ret; - } - - virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override { - int export_format = int(p_preset->get("custom_template/export_format")); - bool should_sign = p_preset->get("package/signed"); - return export_project_helper(p_preset, p_debug, p_path, export_format, should_sign, p_flags); - } - - Error export_project_helper(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int export_format, bool should_sign, int p_flags) { - ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags); - - String src_apk; - Error err; - - EditorProgress ep("export", TTR("Exporting for Android"), 105, true); - - bool use_custom_build = bool(p_preset->get("custom_template/use_custom_build")); - bool p_give_internet = p_flags & (DEBUG_FLAG_DUMB_CLIENT | DEBUG_FLAG_REMOTE_DEBUG); - bool apk_expansion = p_preset->get("apk_expansion/enable"); - Vector<String> enabled_abis = get_enabled_abis(p_preset); - - print_verbose("Exporting for Android..."); - print_verbose("- debug build: " + bool_to_string(p_debug)); - print_verbose("- export path: " + p_path); - print_verbose("- export format: " + itos(export_format)); - print_verbose("- sign build: " + bool_to_string(should_sign)); - print_verbose("- custom build enabled: " + bool_to_string(use_custom_build)); - print_verbose("- apk expansion enabled: " + bool_to_string(apk_expansion)); - print_verbose("- enabled abis: " + String(",").join(enabled_abis)); - print_verbose("- export filter: " + itos(p_preset->get_export_filter())); - print_verbose("- include filter: " + p_preset->get_include_filter()); - print_verbose("- exclude filter: " + p_preset->get_exclude_filter()); - - Ref<Image> splash_image; - Ref<Image> splash_bg_color_image; - String processed_splash_config_xml = load_splash_refs(splash_image, splash_bg_color_image); - - Ref<Image> main_image; - Ref<Image> foreground; - Ref<Image> background; - - load_icon_refs(p_preset, main_image, foreground, background); - - Vector<uint8_t> command_line_flags; - // Write command line flags into the command_line_flags variable. - get_command_line_flags(p_preset, p_path, p_flags, command_line_flags); - - if (export_format == EXPORT_FORMAT_AAB) { - if (!p_path.ends_with(".aab")) { - EditorNode::get_singleton()->show_warning(TTR("Invalid filename! Android App Bundle requires the *.aab extension.")); - return ERR_UNCONFIGURED; - } - if (apk_expansion) { - EditorNode::get_singleton()->show_warning(TTR("APK Expansion not compatible with Android App Bundle.")); - return ERR_UNCONFIGURED; - } - } - if (export_format == EXPORT_FORMAT_APK && !p_path.ends_with(".apk")) { - EditorNode::get_singleton()->show_warning( - TTR("Invalid filename! Android APK requires the *.apk extension.")); - return ERR_UNCONFIGURED; - } - if (export_format > EXPORT_FORMAT_AAB || export_format < EXPORT_FORMAT_APK) { - EditorNode::add_io_error(TTR("Unsupported export format!\n")); - return ERR_UNCONFIGURED; //TODO: is this the right error? - } - - if (use_custom_build) { - print_verbose("Starting custom build.."); - //test that installed build version is alright - { - print_verbose("Checking build version.."); - FileAccessRef f = FileAccess::open("res://android/.build_version", FileAccess::READ); - if (!f) { - EditorNode::get_singleton()->show_warning(TTR("Trying to build from a custom built template, but no version info for it exists. Please reinstall from the 'Project' menu.")); - return ERR_UNCONFIGURED; - } - String version = f->get_line().strip_edges(); - print_verbose("- build version: " + version); - f->close(); - if (version != VERSION_FULL_CONFIG) { - EditorNode::get_singleton()->show_warning(vformat(TTR("Android build version mismatch:\n Template installed: %s\n Godot Version: %s\nPlease reinstall Android build template from 'Project' menu."), version, VERSION_FULL_CONFIG)); - return ERR_UNCONFIGURED; - } - } - String sdk_path = EDITOR_GET("export/android/android_sdk_path"); - ERR_FAIL_COND_V_MSG(sdk_path.is_empty(), ERR_UNCONFIGURED, "Android SDK path must be configured in Editor Settings at 'export/android/android_sdk_path'."); - print_verbose("Android sdk path: " + sdk_path); - - // TODO: should we use "package/name" or "application/config/name"? - String project_name = get_project_name(p_preset->get("package/name")); - err = _create_project_name_strings_files(p_preset, project_name); //project name localization. - if (err != OK) { - EditorNode::add_io_error(TTR("Unable to overwrite res://android/build/res/*.xml files with project name")); - } - // Copies the project icon files into the appropriate Gradle project directory. - _copy_icons_to_gradle_project(p_preset, processed_splash_config_xml, splash_image, splash_bg_color_image, main_image, foreground, background); - // Write an AndroidManifest.xml file into the Gradle project directory. - _write_tmp_manifest(p_preset, p_give_internet, p_debug); - - //stores all the project files inside the Gradle project directory. Also includes all ABIs - _clear_assets_directory(); - _remove_copied_libs(); - if (!apk_expansion) { - print_verbose("Exporting project files.."); - CustomExportData user_data; - user_data.debug = p_debug; - err = export_project_files(p_preset, rename_and_store_file_in_gradle_project, &user_data, copy_gradle_so); - if (err != OK) { - EditorNode::add_io_error(TTR("Could not export project files to gradle project\n")); - return err; - } - if (user_data.libs.size() > 0) { - FileAccessRef fa = FileAccess::open(GDNATIVE_LIBS_PATH, FileAccess::WRITE); - JSON json; - fa->store_string(json.stringify(user_data.libs, "\t")); - fa->close(); - } - } else { - print_verbose("Saving apk expansion file.."); - err = save_apk_expansion_file(p_preset, p_path); - if (err != OK) { - EditorNode::add_io_error(TTR("Could not write expansion package file!")); - return err; - } - } - print_verbose("Storing command line flags.."); - store_file_at_path("res://android/build/assets/_cl_", command_line_flags); - - print_verbose("Updating ANDROID_HOME environment to " + sdk_path); - OS::get_singleton()->set_environment("ANDROID_HOME", sdk_path); //set and overwrite if required - String build_command; - -#ifdef WINDOWS_ENABLED - build_command = "gradlew.bat"; -#else - build_command = "gradlew"; -#endif - - String build_path = ProjectSettings::get_singleton()->get_resource_path().plus_file("android/build"); - build_command = build_path.plus_file(build_command); - - String package_name = get_package_name(p_preset->get("package/unique_name")); - String version_code = itos(p_preset->get("version/code")); - String version_name = p_preset->get("version/name"); - String enabled_abi_string = String("|").join(enabled_abis); - String sign_flag = should_sign ? "true" : "false"; - String zipalign_flag = "true"; - - Vector<PluginConfigAndroid> enabled_plugins = get_enabled_plugins(p_preset); - String local_plugins_binaries = get_plugins_binaries(PluginConfigAndroid::BINARY_TYPE_LOCAL, enabled_plugins); - String remote_plugins_binaries = get_plugins_binaries(PluginConfigAndroid::BINARY_TYPE_REMOTE, enabled_plugins); - String custom_maven_repos = get_plugins_custom_maven_repos(enabled_plugins); - bool clean_build_required = is_clean_build_required(enabled_plugins); - - List<String> cmdline; - if (clean_build_required) { - cmdline.push_back("clean"); - } - - String build_type = p_debug ? "Debug" : "Release"; - if (export_format == EXPORT_FORMAT_AAB) { - String bundle_build_command = vformat("bundle%s", build_type); - cmdline.push_back(bundle_build_command); - } else if (export_format == EXPORT_FORMAT_APK) { - String apk_build_command = vformat("assemble%s", build_type); - cmdline.push_back(apk_build_command); - } - - cmdline.push_back("-p"); // argument to specify the start directory. - cmdline.push_back(build_path); // start directory. - cmdline.push_back("-Pexport_package_name=" + package_name); // argument to specify the package name. - cmdline.push_back("-Pexport_version_code=" + version_code); // argument to specify the version code. - cmdline.push_back("-Pexport_version_name=" + version_name); // argument to specify the version name. - cmdline.push_back("-Pexport_enabled_abis=" + enabled_abi_string); // argument to specify enabled ABIs. - cmdline.push_back("-Pplugins_local_binaries=" + local_plugins_binaries); // argument to specify the list of plugins local dependencies. - cmdline.push_back("-Pplugins_remote_binaries=" + remote_plugins_binaries); // argument to specify the list of plugins remote dependencies. - cmdline.push_back("-Pplugins_maven_repos=" + custom_maven_repos); // argument to specify the list of custom maven repos for the plugins dependencies. - cmdline.push_back("-Pperform_zipalign=" + zipalign_flag); // argument to specify whether the build should be zipaligned. - cmdline.push_back("-Pperform_signing=" + sign_flag); // argument to specify whether the build should be signed. - cmdline.push_back("-Pgodot_editor_version=" + String(VERSION_FULL_CONFIG)); - - // NOTE: The release keystore is not included in the verbose logging - // to avoid accidentally leaking sensitive information when sharing verbose logs for troubleshooting. - // Any non-sensitive additions to the command line arguments must be done above this section. - // Sensitive additions must be done below the logging statement. - print_verbose("Build Android project using gradle command: " + String("\n") + build_command + " " + join_list(cmdline, String(" "))); - - if (should_sign) { - if (p_debug) { - String debug_keystore = p_preset->get("keystore/debug"); - String debug_password = p_preset->get("keystore/debug_password"); - String debug_user = p_preset->get("keystore/debug_user"); - - if (debug_keystore.is_empty()) { - debug_keystore = EditorSettings::get_singleton()->get("export/android/debug_keystore"); - debug_password = EditorSettings::get_singleton()->get("export/android/debug_keystore_pass"); - debug_user = EditorSettings::get_singleton()->get("export/android/debug_keystore_user"); - } - - cmdline.push_back("-Pdebug_keystore_file=" + debug_keystore); // argument to specify the debug keystore file. - cmdline.push_back("-Pdebug_keystore_alias=" + debug_user); // argument to specify the debug keystore alias. - cmdline.push_back("-Pdebug_keystore_password=" + debug_password); // argument to specify the debug keystore password. - } else { - // Pass the release keystore info as well - String release_keystore = p_preset->get("keystore/release"); - String release_username = p_preset->get("keystore/release_user"); - String release_password = p_preset->get("keystore/release_password"); - if (!FileAccess::exists(release_keystore)) { - EditorNode::add_io_error(TTR("Could not find keystore, unable to export.")); - return ERR_FILE_CANT_OPEN; - } - - cmdline.push_back("-Prelease_keystore_file=" + release_keystore); // argument to specify the release keystore file. - cmdline.push_back("-Prelease_keystore_alias=" + release_username); // argument to specify the release keystore alias. - cmdline.push_back("-Prelease_keystore_password=" + release_password); // argument to specify the release keystore password. - } - } - - int result = EditorNode::get_singleton()->execute_and_show_output(TTR("Building Android Project (gradle)"), build_command, cmdline); - if (result != 0) { - EditorNode::get_singleton()->show_warning(TTR("Building of Android project failed, check output for the error.\nAlternatively visit docs.godotengine.org for Android build documentation.")); - return ERR_CANT_CREATE; - } - - List<String> copy_args; - String copy_command; - if (export_format == EXPORT_FORMAT_AAB) { - copy_command = vformat("copyAndRename%sAab", build_type); - } else if (export_format == EXPORT_FORMAT_APK) { - copy_command = vformat("copyAndRename%sApk", build_type); - } - - copy_args.push_back(copy_command); - - copy_args.push_back("-p"); // argument to specify the start directory. - copy_args.push_back(build_path); // start directory. - - String export_filename = p_path.get_file(); - String export_path = p_path.get_base_dir(); - if (export_path.is_rel_path()) { - export_path = OS::get_singleton()->get_resource_dir().plus_file(export_path); - } - export_path = ProjectSettings::get_singleton()->globalize_path(export_path).simplify_path(); - - copy_args.push_back("-Pexport_path=file:" + export_path); - copy_args.push_back("-Pexport_filename=" + export_filename); - - print_verbose("Copying Android binary using gradle command: " + String("\n") + build_command + " " + join_list(copy_args, String(" "))); - int copy_result = EditorNode::get_singleton()->execute_and_show_output(TTR("Moving output"), build_command, copy_args); - if (copy_result != 0) { - EditorNode::get_singleton()->show_warning(TTR("Unable to copy and rename export file, check gradle project directory for outputs.")); - return ERR_CANT_CREATE; - } - - print_verbose("Successfully completed Android custom build."); - return OK; - } - // This is the start of the Legacy build system - print_verbose("Starting legacy build system.."); - if (p_debug) { - src_apk = p_preset->get("custom_template/debug"); - } else { - src_apk = p_preset->get("custom_template/release"); - } - src_apk = src_apk.strip_edges(); - if (src_apk == "") { - if (p_debug) { - src_apk = find_export_template("android_debug.apk"); - } else { - src_apk = find_export_template("android_release.apk"); - } - if (src_apk == "") { - EditorNode::add_io_error(vformat(TTR("Package not found: %s"), src_apk)); - return ERR_FILE_NOT_FOUND; - } - } - - if (!DirAccess::exists(p_path.get_base_dir())) { - return ERR_FILE_BAD_PATH; - } - - FileAccess *src_f = nullptr; - zlib_filefunc_def io = zipio_create_io_from_file(&src_f); - - if (ep.step(TTR("Creating APK..."), 0)) { - return ERR_SKIP; - } - - unzFile pkg = unzOpen2(src_apk.utf8().get_data(), &io); - if (!pkg) { - EditorNode::add_io_error(vformat(TTR("Could not find template APK to export:\n%s"), src_apk)); - return ERR_FILE_NOT_FOUND; - } - - int ret = unzGoToFirstFile(pkg); - - zlib_filefunc_def io2 = io; - FileAccess *dst_f = nullptr; - io2.opaque = &dst_f; - - String tmp_unaligned_path = EditorPaths::get_singleton()->get_cache_dir().plus_file("tmpexport-unaligned." + uitos(OS::get_singleton()->get_unix_time()) + ".apk"); - -#define CLEANUP_AND_RETURN(m_err) \ - { \ - DirAccess::remove_file_or_error(tmp_unaligned_path); \ - return m_err; \ - } - - zipFile unaligned_apk = zipOpen2(tmp_unaligned_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io2); - - String cmdline = p_preset->get("command_line/extra_args"); - - String version_name = p_preset->get("version/name"); - String package_name = p_preset->get("package/unique_name"); - - String apk_expansion_pkey = p_preset->get("apk_expansion/public_key"); - - Vector<String> invalid_abis(enabled_abis); - while (ret == UNZ_OK) { - //get filename - unz_file_info info; - char fname[16384]; - ret = unzGetCurrentFileInfo(pkg, &info, fname, 16384, nullptr, 0, nullptr, 0); - - bool skip = false; - - String file = fname; - - Vector<uint8_t> data; - data.resize(info.uncompressed_size); - - //read - unzOpenCurrentFile(pkg); - unzReadCurrentFile(pkg, data.ptrw(), data.size()); - unzCloseCurrentFile(pkg); - - //write - if (file == "AndroidManifest.xml") { - _fix_manifest(p_preset, data, p_give_internet); - } - if (file == "resources.arsc") { - _fix_resources(p_preset, data); - } - - // Process the splash image - if ((file == SPLASH_IMAGE_EXPORT_PATH || file == LEGACY_BUILD_SPLASH_IMAGE_EXPORT_PATH) && splash_image.is_valid() && !splash_image->is_empty()) { - _load_image_data(splash_image, data); - } - - // Process the splash bg color image - if ((file == SPLASH_BG_COLOR_PATH || file == LEGACY_BUILD_SPLASH_BG_COLOR_PATH) && splash_bg_color_image.is_valid() && !splash_bg_color_image->is_empty()) { - _load_image_data(splash_bg_color_image, data); - } - - for (int i = 0; i < icon_densities_count; ++i) { - if (main_image.is_valid() && !main_image->is_empty()) { - if (file == launcher_icons[i].export_path) { - _process_launcher_icons(file, main_image, launcher_icons[i].dimensions, data); - } - } - if (foreground.is_valid() && !foreground->is_empty()) { - if (file == launcher_adaptive_icon_foregrounds[i].export_path) { - _process_launcher_icons(file, foreground, launcher_adaptive_icon_foregrounds[i].dimensions, data); - } - } - if (background.is_valid() && !background->is_empty()) { - if (file == launcher_adaptive_icon_backgrounds[i].export_path) { - _process_launcher_icons(file, background, launcher_adaptive_icon_backgrounds[i].dimensions, data); - } - } - } - - if (file.ends_with(".so")) { - bool enabled = false; - for (int i = 0; i < enabled_abis.size(); ++i) { - if (file.begins_with("lib/" + enabled_abis[i] + "/")) { - invalid_abis.erase(enabled_abis[i]); - enabled = true; - break; - } - } - if (!enabled) { - skip = true; - } - } - - if (file.begins_with("META-INF") && should_sign) { - skip = true; - } - - if (!skip) { - print_line("ADDING: " + file); - - // Respect decision on compression made by AAPT for the export template - const bool uncompressed = info.compression_method == 0; - - zip_fileinfo zipfi = get_zip_fileinfo(); - - zipOpenNewFileInZip(unaligned_apk, - file.utf8().get_data(), - &zipfi, - nullptr, - 0, - nullptr, - 0, - nullptr, - uncompressed ? 0 : Z_DEFLATED, - Z_DEFAULT_COMPRESSION); - - zipWriteInFileInZip(unaligned_apk, data.ptr(), data.size()); - zipCloseFileInZip(unaligned_apk); - } - - ret = unzGoToNextFile(pkg); - } - - if (!invalid_abis.is_empty()) { - String unsupported_arch = String(", ").join(invalid_abis); - EditorNode::add_io_error(vformat(TTR("Missing libraries in the export template for the selected architectures: %s.\nPlease build a template with all required libraries, or uncheck the missing architectures in the export preset."), unsupported_arch)); - CLEANUP_AND_RETURN(ERR_FILE_NOT_FOUND); - } - - if (ep.step(TTR("Adding files..."), 1)) { - CLEANUP_AND_RETURN(ERR_SKIP); - } - err = OK; - - if (p_flags & DEBUG_FLAG_DUMB_CLIENT) { - APKExportData ed; - ed.ep = &ep; - ed.apk = unaligned_apk; - err = export_project_files(p_preset, ignore_apk_file, &ed, save_apk_so); - } else { - if (apk_expansion) { - err = save_apk_expansion_file(p_preset, p_path); - if (err != OK) { - EditorNode::add_io_error(TTR("Could not write expansion package file!")); - return err; - } - } else { - APKExportData ed; - ed.ep = &ep; - ed.apk = unaligned_apk; - err = export_project_files(p_preset, save_apk_file, &ed, save_apk_so); - } - } - - if (err != OK) { - unzClose(pkg); - EditorNode::add_io_error(TTR("Could not export project files")); - CLEANUP_AND_RETURN(ERR_SKIP); - } - - zip_fileinfo zipfi = get_zip_fileinfo(); - zipOpenNewFileInZip(unaligned_apk, - "assets/_cl_", - &zipfi, - nullptr, - 0, - nullptr, - 0, - nullptr, - 0, // No compress (little size gain and potentially slower startup) - Z_DEFAULT_COMPRESSION); - zipWriteInFileInZip(unaligned_apk, command_line_flags.ptr(), command_line_flags.size()); - zipCloseFileInZip(unaligned_apk); - zipClose(unaligned_apk, nullptr); - unzClose(pkg); - - if (err != OK) { - CLEANUP_AND_RETURN(err); - } - - // Let's zip-align (must be done before signing) - - static const int ZIP_ALIGNMENT = 4; - - // If we're not signing the apk, then the next step should be the last. - const int next_step = should_sign ? 103 : 105; - if (ep.step(TTR("Aligning APK..."), next_step)) { - CLEANUP_AND_RETURN(ERR_SKIP); - } - - unzFile tmp_unaligned = unzOpen2(tmp_unaligned_path.utf8().get_data(), &io); - if (!tmp_unaligned) { - EditorNode::add_io_error(TTR("Could not unzip temporary unaligned APK.")); - CLEANUP_AND_RETURN(ERR_FILE_NOT_FOUND); - } - - ret = unzGoToFirstFile(tmp_unaligned); - - io2 = io; - dst_f = nullptr; - io2.opaque = &dst_f; - zipFile final_apk = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io2); - - // Take files from the unaligned APK and write them out to the aligned one - // in raw mode, i.e. not uncompressing and recompressing, aligning them as needed, - // following what is done in https://github.com/android/platform_build/blob/master/tools/zipalign/ZipAlign.cpp - int bias = 0; - while (ret == UNZ_OK) { - unz_file_info info; - memset(&info, 0, sizeof(info)); - - char fname[16384]; - char extra[16384]; - ret = unzGetCurrentFileInfo(tmp_unaligned, &info, fname, 16384, extra, 16384 - ZIP_ALIGNMENT, nullptr, 0); - - String file = fname; - - Vector<uint8_t> data; - data.resize(info.compressed_size); - - // read - int method, level; - unzOpenCurrentFile2(tmp_unaligned, &method, &level, 1); // raw read - long file_offset = unzGetCurrentFileZStreamPos64(tmp_unaligned); - unzReadCurrentFile(tmp_unaligned, data.ptrw(), data.size()); - unzCloseCurrentFile(tmp_unaligned); - - // align - int padding = 0; - if (!info.compression_method) { - // Uncompressed file => Align - long new_offset = file_offset + bias; - padding = (ZIP_ALIGNMENT - (new_offset % ZIP_ALIGNMENT)) % ZIP_ALIGNMENT; - } - - memset(extra + info.size_file_extra, 0, padding); - - zip_fileinfo fileinfo = get_zip_fileinfo(); - zipOpenNewFileInZip2(final_apk, - file.utf8().get_data(), - &fileinfo, - extra, - info.size_file_extra + padding, - nullptr, - 0, - nullptr, - method, - level, - 1); // raw write - zipWriteInFileInZip(final_apk, data.ptr(), data.size()); - zipCloseFileInZipRaw(final_apk, info.uncompressed_size, info.crc); - - bias += padding; - - ret = unzGoToNextFile(tmp_unaligned); - } - - zipClose(final_apk, nullptr); - unzClose(tmp_unaligned); - - if (should_sign) { - // Signing must be done last as any additional modifications to the - // file will invalidate the signature. - err = sign_apk(p_preset, p_debug, p_path, ep); - if (err != OK) { - CLEANUP_AND_RETURN(err); - } - } - - CLEANUP_AND_RETURN(OK); - } - - virtual void get_platform_features(List<String> *r_features) override { - r_features->push_back("mobile"); - r_features->push_back("Android"); - } - - virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) override { - } - - EditorExportPlatformAndroid() { - Ref<Image> img = memnew(Image(_android_logo)); - logo.instantiate(); - logo->create_from_image(img); - - img = Ref<Image>(memnew(Image(_android_run_icon))); - run_icon.instantiate(); - run_icon->create_from_image(img); - - devices_changed.set(); - plugins_changed.set(); - check_for_changes_thread.start(_check_for_changes_poll_thread, this); - } - - ~EditorExportPlatformAndroid() { - quit_request.set(); - check_for_changes_thread.wait_to_finish(); - } -}; +#include "export_plugin.h" void register_android_exporter() { String exe_ext; diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp new file mode 100644 index 0000000000..6c2e481856 --- /dev/null +++ b/platform/android/export/export_plugin.cpp @@ -0,0 +1,2945 @@ +/*************************************************************************/ +/* export_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "export_plugin.h" + +static const char *android_perms[] = { + "ACCESS_CHECKIN_PROPERTIES", + "ACCESS_COARSE_LOCATION", + "ACCESS_FINE_LOCATION", + "ACCESS_LOCATION_EXTRA_COMMANDS", + "ACCESS_MOCK_LOCATION", + "ACCESS_NETWORK_STATE", + "ACCESS_SURFACE_FLINGER", + "ACCESS_WIFI_STATE", + "ACCOUNT_MANAGER", + "ADD_VOICEMAIL", + "AUTHENTICATE_ACCOUNTS", + "BATTERY_STATS", + "BIND_ACCESSIBILITY_SERVICE", + "BIND_APPWIDGET", + "BIND_DEVICE_ADMIN", + "BIND_INPUT_METHOD", + "BIND_NFC_SERVICE", + "BIND_NOTIFICATION_LISTENER_SERVICE", + "BIND_PRINT_SERVICE", + "BIND_REMOTEVIEWS", + "BIND_TEXT_SERVICE", + "BIND_VPN_SERVICE", + "BIND_WALLPAPER", + "BLUETOOTH", + "BLUETOOTH_ADMIN", + "BLUETOOTH_PRIVILEGED", + "BRICK", + "BROADCAST_PACKAGE_REMOVED", + "BROADCAST_SMS", + "BROADCAST_STICKY", + "BROADCAST_WAP_PUSH", + "CALL_PHONE", + "CALL_PRIVILEGED", + "CAMERA", + "CAPTURE_AUDIO_OUTPUT", + "CAPTURE_SECURE_VIDEO_OUTPUT", + "CAPTURE_VIDEO_OUTPUT", + "CHANGE_COMPONENT_ENABLED_STATE", + "CHANGE_CONFIGURATION", + "CHANGE_NETWORK_STATE", + "CHANGE_WIFI_MULTICAST_STATE", + "CHANGE_WIFI_STATE", + "CLEAR_APP_CACHE", + "CLEAR_APP_USER_DATA", + "CONTROL_LOCATION_UPDATES", + "DELETE_CACHE_FILES", + "DELETE_PACKAGES", + "DEVICE_POWER", + "DIAGNOSTIC", + "DISABLE_KEYGUARD", + "DUMP", + "EXPAND_STATUS_BAR", + "FACTORY_TEST", + "FLASHLIGHT", + "FORCE_BACK", + "GET_ACCOUNTS", + "GET_PACKAGE_SIZE", + "GET_TASKS", + "GET_TOP_ACTIVITY_INFO", + "GLOBAL_SEARCH", + "HARDWARE_TEST", + "INJECT_EVENTS", + "INSTALL_LOCATION_PROVIDER", + "INSTALL_PACKAGES", + "INSTALL_SHORTCUT", + "INTERNAL_SYSTEM_WINDOW", + "INTERNET", + "KILL_BACKGROUND_PROCESSES", + "LOCATION_HARDWARE", + "MANAGE_ACCOUNTS", + "MANAGE_APP_TOKENS", + "MANAGE_DOCUMENTS", + "MASTER_CLEAR", + "MEDIA_CONTENT_CONTROL", + "MODIFY_AUDIO_SETTINGS", + "MODIFY_PHONE_STATE", + "MOUNT_FORMAT_FILESYSTEMS", + "MOUNT_UNMOUNT_FILESYSTEMS", + "NFC", + "PERSISTENT_ACTIVITY", + "PROCESS_OUTGOING_CALLS", + "READ_CALENDAR", + "READ_CALL_LOG", + "READ_CONTACTS", + "READ_EXTERNAL_STORAGE", + "READ_FRAME_BUFFER", + "READ_HISTORY_BOOKMARKS", + "READ_INPUT_STATE", + "READ_LOGS", + "READ_PHONE_STATE", + "READ_PROFILE", + "READ_SMS", + "READ_SOCIAL_STREAM", + "READ_SYNC_SETTINGS", + "READ_SYNC_STATS", + "READ_USER_DICTIONARY", + "REBOOT", + "RECEIVE_BOOT_COMPLETED", + "RECEIVE_MMS", + "RECEIVE_SMS", + "RECEIVE_WAP_PUSH", + "RECORD_AUDIO", + "REORDER_TASKS", + "RESTART_PACKAGES", + "SEND_RESPOND_VIA_MESSAGE", + "SEND_SMS", + "SET_ACTIVITY_WATCHER", + "SET_ALARM", + "SET_ALWAYS_FINISH", + "SET_ANIMATION_SCALE", + "SET_DEBUG_APP", + "SET_ORIENTATION", + "SET_POINTER_SPEED", + "SET_PREFERRED_APPLICATIONS", + "SET_PROCESS_LIMIT", + "SET_TIME", + "SET_TIME_ZONE", + "SET_WALLPAPER", + "SET_WALLPAPER_HINTS", + "SIGNAL_PERSISTENT_PROCESSES", + "STATUS_BAR", + "SUBSCRIBED_FEEDS_READ", + "SUBSCRIBED_FEEDS_WRITE", + "SYSTEM_ALERT_WINDOW", + "TRANSMIT_IR", + "UNINSTALL_SHORTCUT", + "UPDATE_DEVICE_STATS", + "USE_CREDENTIALS", + "USE_SIP", + "VIBRATE", + "WAKE_LOCK", + "WRITE_APN_SETTINGS", + "WRITE_CALENDAR", + "WRITE_CALL_LOG", + "WRITE_CONTACTS", + "WRITE_EXTERNAL_STORAGE", + "WRITE_GSERVICES", + "WRITE_HISTORY_BOOKMARKS", + "WRITE_PROFILE", + "WRITE_SECURE_SETTINGS", + "WRITE_SETTINGS", + "WRITE_SMS", + "WRITE_SOCIAL_STREAM", + "WRITE_SYNC_SETTINGS", + "WRITE_USER_DICTIONARY", + nullptr +}; + +static const char *SPLASH_IMAGE_EXPORT_PATH = "res/drawable-nodpi/splash.png"; +static const char *LEGACY_BUILD_SPLASH_IMAGE_EXPORT_PATH = "res/drawable-nodpi-v4/splash.png"; +static const char *SPLASH_BG_COLOR_PATH = "res/drawable-nodpi/splash_bg_color.png"; +static const char *LEGACY_BUILD_SPLASH_BG_COLOR_PATH = "res/drawable-nodpi-v4/splash_bg_color.png"; +static const char *SPLASH_CONFIG_PATH = "res://android/build/res/drawable/splash_drawable.xml"; +static const char *GDNATIVE_LIBS_PATH = "res://android/build/libs/gdnativelibs.json"; + +static const int icon_densities_count = 6; +static const char *launcher_icon_option = "launcher_icons/main_192x192"; +static const char *launcher_adaptive_icon_foreground_option = "launcher_icons/adaptive_foreground_432x432"; +static const char *launcher_adaptive_icon_background_option = "launcher_icons/adaptive_background_432x432"; + +static const LauncherIcon launcher_icons[icon_densities_count] = { + { "res/mipmap-xxxhdpi-v4/icon.png", 192 }, + { "res/mipmap-xxhdpi-v4/icon.png", 144 }, + { "res/mipmap-xhdpi-v4/icon.png", 96 }, + { "res/mipmap-hdpi-v4/icon.png", 72 }, + { "res/mipmap-mdpi-v4/icon.png", 48 }, + { "res/mipmap/icon.png", 192 } +}; + +static const LauncherIcon launcher_adaptive_icon_foregrounds[icon_densities_count] = { + { "res/mipmap-xxxhdpi-v4/icon_foreground.png", 432 }, + { "res/mipmap-xxhdpi-v4/icon_foreground.png", 324 }, + { "res/mipmap-xhdpi-v4/icon_foreground.png", 216 }, + { "res/mipmap-hdpi-v4/icon_foreground.png", 162 }, + { "res/mipmap-mdpi-v4/icon_foreground.png", 108 }, + { "res/mipmap/icon_foreground.png", 432 } +}; + +static const LauncherIcon launcher_adaptive_icon_backgrounds[icon_densities_count] = { + { "res/mipmap-xxxhdpi-v4/icon_background.png", 432 }, + { "res/mipmap-xxhdpi-v4/icon_background.png", 324 }, + { "res/mipmap-xhdpi-v4/icon_background.png", 216 }, + { "res/mipmap-hdpi-v4/icon_background.png", 162 }, + { "res/mipmap-mdpi-v4/icon_background.png", 108 }, + { "res/mipmap/icon_background.png", 432 } +}; + +static const int EXPORT_FORMAT_APK = 0; +static const int EXPORT_FORMAT_AAB = 1; + +void EditorExportPlatformAndroid::_check_for_changes_poll_thread(void *ud) { + EditorExportPlatformAndroid *ea = (EditorExportPlatformAndroid *)ud; + + while (!ea->quit_request.is_set()) { + // Check for plugins updates + { + // Nothing to do if we already know the plugins have changed. + if (!ea->plugins_changed.is_set()) { + Vector<PluginConfigAndroid> loaded_plugins = get_plugins(); + + MutexLock lock(ea->plugins_lock); + + if (ea->plugins.size() != loaded_plugins.size()) { + ea->plugins_changed.set(); + } else { + for (int i = 0; i < ea->plugins.size(); i++) { + if (ea->plugins[i].name != loaded_plugins[i].name) { + ea->plugins_changed.set(); + break; + } + } + } + + if (ea->plugins_changed.is_set()) { + ea->plugins = loaded_plugins; + } + } + } + + // Check for devices updates + String adb = get_adb_path(); + if (FileAccess::exists(adb)) { + String devices; + List<String> args; + args.push_back("devices"); + int ec; + OS::get_singleton()->execute(adb, args, &devices, &ec); + + Vector<String> ds = devices.split("\n"); + Vector<String> ldevices; + for (int i = 1; i < ds.size(); i++) { + String d = ds[i]; + int dpos = d.find("device"); + if (dpos == -1) { + continue; + } + d = d.substr(0, dpos).strip_edges(); + ldevices.push_back(d); + } + + MutexLock lock(ea->device_lock); + + bool different = false; + + if (ea->devices.size() != ldevices.size()) { + different = true; + } else { + for (int i = 0; i < ea->devices.size(); i++) { + if (ea->devices[i].id != ldevices[i]) { + different = true; + break; + } + } + } + + if (different) { + Vector<Device> ndevices; + + for (int i = 0; i < ldevices.size(); i++) { + Device d; + d.id = ldevices[i]; + for (int j = 0; j < ea->devices.size(); j++) { + if (ea->devices[j].id == ldevices[i]) { + d.description = ea->devices[j].description; + d.name = ea->devices[j].name; + d.api_level = ea->devices[j].api_level; + } + } + + if (d.description == "") { + //in the oven, request! + args.clear(); + args.push_back("-s"); + args.push_back(d.id); + args.push_back("shell"); + args.push_back("getprop"); + int ec2; + String dp; + + OS::get_singleton()->execute(adb, args, &dp, &ec2); + + Vector<String> props = dp.split("\n"); + String vendor; + String device; + d.description = "Device ID: " + d.id + "\n"; + d.api_level = 0; + for (int j = 0; j < props.size(); j++) { + // got information by `shell cat /system/build.prop` before and its format is "property=value" + // it's now changed to use `shell getporp` because of permission issue with Android 8.0 and above + // its format is "[property]: [value]" so changed it as like build.prop + String p = props[j]; + p = p.replace("]: ", "="); + p = p.replace("[", ""); + p = p.replace("]", ""); + + if (p.begins_with("ro.product.model=")) { + device = p.get_slice("=", 1).strip_edges(); + } else if (p.begins_with("ro.product.brand=")) { + vendor = p.get_slice("=", 1).strip_edges().capitalize(); + } else if (p.begins_with("ro.build.display.id=")) { + d.description += "Build: " + p.get_slice("=", 1).strip_edges() + "\n"; + } else if (p.begins_with("ro.build.version.release=")) { + d.description += "Release: " + p.get_slice("=", 1).strip_edges() + "\n"; + } else if (p.begins_with("ro.build.version.sdk=")) { + d.api_level = p.get_slice("=", 1).to_int(); + } else if (p.begins_with("ro.product.cpu.abi=")) { + d.description += "CPU: " + p.get_slice("=", 1).strip_edges() + "\n"; + } else if (p.begins_with("ro.product.manufacturer=")) { + d.description += "Manufacturer: " + p.get_slice("=", 1).strip_edges() + "\n"; + } else if (p.begins_with("ro.board.platform=")) { + d.description += "Chipset: " + p.get_slice("=", 1).strip_edges() + "\n"; + } else if (p.begins_with("ro.opengles.version=")) { + uint32_t opengl = p.get_slice("=", 1).to_int(); + d.description += "OpenGL: " + itos(opengl >> 16) + "." + itos((opengl >> 8) & 0xFF) + "." + itos((opengl)&0xFF) + "\n"; + } + } + + d.name = vendor + " " + device; + if (device == String()) { + continue; + } + } + + ndevices.push_back(d); + } + + ea->devices = ndevices; + ea->devices_changed.set(); + } + } + + uint64_t sleep = 200; + uint64_t wait = 3000000; + uint64_t time = OS::get_singleton()->get_ticks_usec(); + while (OS::get_singleton()->get_ticks_usec() - time < wait) { + OS::get_singleton()->delay_usec(1000 * sleep); + if (ea->quit_request.is_set()) { + break; + } + } + } + + if (EditorSettings::get_singleton()->get("export/android/shutdown_adb_on_exit")) { + String adb = get_adb_path(); + if (!FileAccess::exists(adb)) { + return; //adb not configured + } + + List<String> args; + args.push_back("kill-server"); + OS::get_singleton()->execute(adb, args); + }; +} + +String EditorExportPlatformAndroid::get_project_name(const String &p_name) const { + String aname; + if (p_name != "") { + aname = p_name; + } else { + aname = ProjectSettings::get_singleton()->get("application/config/name"); + } + + if (aname == "") { + aname = VERSION_NAME; + } + + return aname; +} + +String EditorExportPlatformAndroid::get_package_name(const String &p_package) const { + String pname = p_package; + String basename = ProjectSettings::get_singleton()->get("application/config/name"); + basename = basename.to_lower(); + + String name; + bool first = true; + for (int i = 0; i < basename.length(); i++) { + char32_t c = basename[i]; + if (c >= '0' && c <= '9' && first) { + continue; + } + if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) { + name += String::chr(c); + first = false; + } + } + if (name == "") { + name = "noname"; + } + + pname = pname.replace("$genname", name); + + return pname; +} + +bool EditorExportPlatformAndroid::is_package_name_valid(const String &p_package, String *r_error) const { + String pname = p_package; + + if (pname.length() == 0) { + if (r_error) { + *r_error = TTR("Package name is missing."); + } + return false; + } + + int segments = 0; + bool first = true; + for (int i = 0; i < pname.length(); i++) { + char32_t c = pname[i]; + if (first && c == '.') { + if (r_error) { + *r_error = TTR("Package segments must be of non-zero length."); + } + return false; + } + if (c == '.') { + segments++; + first = true; + continue; + } + if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_')) { + if (r_error) { + *r_error = vformat(TTR("The character '%s' is not allowed in Android application package names."), String::chr(c)); + } + return false; + } + if (first && (c >= '0' && c <= '9')) { + if (r_error) { + *r_error = TTR("A digit cannot be the first character in a package segment."); + } + return false; + } + if (first && c == '_') { + if (r_error) { + *r_error = vformat(TTR("The character '%s' cannot be the first character in a package segment."), String::chr(c)); + } + return false; + } + first = false; + } + + if (segments == 0) { + if (r_error) { + *r_error = TTR("The package must have at least one '.' separator."); + } + return false; + } + + if (first) { + if (r_error) { + *r_error = TTR("Package segments must be of non-zero length."); + } + return false; + } + + return true; +} + +bool EditorExportPlatformAndroid::_should_compress_asset(const String &p_path, const Vector<uint8_t> &p_data) { + /* + * By not compressing files with little or not benefit in doing so, + * a performance gain is expected attime. Moreover, if the APK is + * zip-aligned, assets stored as they are can be efficiently read by + * Android by memory-mapping them. + */ + + // -- Unconditional uncompress to mimic AAPT plus some other + + static const char *unconditional_compress_ext[] = { + // From https://github.com/android/platform_frameworks_base/blob/master/tools/aapt/Package.cpp + // These formats are already compressed, or don't compress well: + ".jpg", ".jpeg", ".png", ".gif", + ".wav", ".mp2", ".mp3", ".ogg", ".aac", + ".mpg", ".mpeg", ".mid", ".midi", ".smf", ".jet", + ".rtttl", ".imy", ".xmf", ".mp4", ".m4a", + ".m4v", ".3gp", ".3gpp", ".3g2", ".3gpp2", + ".amr", ".awb", ".wma", ".wmv", + // Godot-specific: + ".webp", // Same reasoning as .png + ".cfb", // Don't let small config files slow-down startup + ".scn", // Binary scenes are usually already compressed + ".stex", // Streamable textures are usually already compressed + // Trailer for easier processing + nullptr + }; + + for (const char **ext = unconditional_compress_ext; *ext; ++ext) { + if (p_path.to_lower().ends_with(String(*ext))) { + return false; + } + } + + // -- Compressed resource? + + if (p_data.size() >= 4 && p_data[0] == 'R' && p_data[1] == 'S' && p_data[2] == 'C' && p_data[3] == 'C') { + // Already compressed + return false; + } + + // --- TODO: Decide on texture resources according to their image compression setting + + return true; +} + +zip_fileinfo EditorExportPlatformAndroid::get_zip_fileinfo() { + OS::Time time = OS::get_singleton()->get_time(); + OS::Date date = OS::get_singleton()->get_date(); + + zip_fileinfo zipfi; + zipfi.tmz_date.tm_hour = time.hour; + zipfi.tmz_date.tm_mday = date.day; + zipfi.tmz_date.tm_min = time.minute; + zipfi.tmz_date.tm_mon = date.month - 1; // tm_mon is zero indexed + zipfi.tmz_date.tm_sec = time.second; + zipfi.tmz_date.tm_year = date.year; + zipfi.dosDate = 0; + zipfi.external_fa = 0; + zipfi.internal_fa = 0; + + return zipfi; +} + +Vector<String> EditorExportPlatformAndroid::get_abis() { + Vector<String> abis; + abis.push_back("armeabi-v7a"); + abis.push_back("arm64-v8a"); + abis.push_back("x86"); + abis.push_back("x86_64"); + return abis; +} + +/// List the gdap files in the directory specified by the p_path parameter. +Vector<String> EditorExportPlatformAndroid::list_gdap_files(const String &p_path) { + Vector<String> dir_files; + DirAccessRef da = DirAccess::open(p_path); + if (da) { + da->list_dir_begin(); + while (true) { + String file = da->get_next(); + if (file == "") { + break; + } + + if (da->current_is_dir() || da->current_is_hidden()) { + continue; + } + + if (file.ends_with(PluginConfigAndroid::PLUGIN_CONFIG_EXT)) { + dir_files.push_back(file); + } + } + da->list_dir_end(); + } + + return dir_files; +} + +Vector<PluginConfigAndroid> EditorExportPlatformAndroid::get_plugins() { + Vector<PluginConfigAndroid> loaded_plugins; + + String plugins_dir = ProjectSettings::get_singleton()->get_resource_path().plus_file("android/plugins"); + + // Add the prebuilt plugins + loaded_plugins.append_array(PluginConfigAndroid::get_prebuilt_plugins(plugins_dir)); + + if (DirAccess::exists(plugins_dir)) { + Vector<String> plugins_filenames = list_gdap_files(plugins_dir); + + if (!plugins_filenames.is_empty()) { + Ref<ConfigFile> config_file = memnew(ConfigFile); + for (int i = 0; i < plugins_filenames.size(); i++) { + PluginConfigAndroid config = PluginConfigAndroid::load_plugin_config(config_file, plugins_dir.plus_file(plugins_filenames[i])); + if (config.valid_config) { + loaded_plugins.push_back(config); + } else { + print_error("Invalid plugin config file " + plugins_filenames[i]); + } + } + } + } + + return loaded_plugins; +} + +Vector<PluginConfigAndroid> EditorExportPlatformAndroid::get_enabled_plugins(const Ref<EditorExportPreset> &p_presets) { + Vector<PluginConfigAndroid> enabled_plugins; + Vector<PluginConfigAndroid> all_plugins = get_plugins(); + for (int i = 0; i < all_plugins.size(); i++) { + PluginConfigAndroid plugin = all_plugins[i]; + bool enabled = p_presets->get("plugins/" + plugin.name); + if (enabled) { + enabled_plugins.push_back(plugin); + } + } + + return enabled_plugins; +} + +Error EditorExportPlatformAndroid::store_in_apk(APKExportData *ed, const String &p_path, const Vector<uint8_t> &p_data, int compression_method) { + zip_fileinfo zipfi = get_zip_fileinfo(); + zipOpenNewFileInZip(ed->apk, + p_path.utf8().get_data(), + &zipfi, + nullptr, + 0, + nullptr, + 0, + nullptr, + compression_method, + Z_DEFAULT_COMPRESSION); + + zipWriteInFileInZip(ed->apk, p_data.ptr(), p_data.size()); + zipCloseFileInZip(ed->apk); + + return OK; +} + +Error EditorExportPlatformAndroid::save_apk_so(void *p_userdata, const SharedObject &p_so) { + if (!p_so.path.get_file().begins_with("lib")) { + String err = "Android .so file names must start with \"lib\", but got: " + p_so.path; + ERR_PRINT(err); + return FAILED; + } + APKExportData *ed = (APKExportData *)p_userdata; + Vector<String> abis = get_abis(); + bool exported = false; + for (int i = 0; i < p_so.tags.size(); ++i) { + // shared objects can be fat (compatible with multiple ABIs) + int abi_index = abis.find(p_so.tags[i]); + if (abi_index != -1) { + exported = true; + String abi = abis[abi_index]; + String dst_path = String("lib").plus_file(abi).plus_file(p_so.path.get_file()); + Vector<uint8_t> array = FileAccess::get_file_as_array(p_so.path); + Error store_err = store_in_apk(ed, dst_path, array); + ERR_FAIL_COND_V_MSG(store_err, store_err, "Cannot store in apk file '" + dst_path + "'."); + } + } + if (!exported) { + String abis_string = String(" ").join(abis); + String err = "Cannot determine ABI for library \"" + p_so.path + "\". One of the supported ABIs must be used as a tag: " + abis_string; + ERR_PRINT(err); + return FAILED; + } + return OK; +} + +Error EditorExportPlatformAndroid::save_apk_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key) { + APKExportData *ed = (APKExportData *)p_userdata; + String dst_path = p_path.replace_first("res://", "assets/"); + + store_in_apk(ed, dst_path, p_data, _should_compress_asset(p_path, p_data) ? Z_DEFLATED : 0); + return OK; +} + +Error EditorExportPlatformAndroid::ignore_apk_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key) { + return OK; +} + +Error EditorExportPlatformAndroid::copy_gradle_so(void *p_userdata, const SharedObject &p_so) { + ERR_FAIL_COND_V_MSG(!p_so.path.get_file().begins_with("lib"), FAILED, + "Android .so file names must start with \"lib\", but got: " + p_so.path); + Vector<String> abis = get_abis(); + CustomExportData *export_data = (CustomExportData *)p_userdata; + bool exported = false; + for (int i = 0; i < p_so.tags.size(); ++i) { + int abi_index = abis.find(p_so.tags[i]); + if (abi_index != -1) { + exported = true; + String base = "res://android/build/libs"; + String type = export_data->debug ? "debug" : "release"; + String abi = abis[abi_index]; + String filename = p_so.path.get_file(); + String dst_path = base.plus_file(type).plus_file(abi).plus_file(filename); + Vector<uint8_t> data = FileAccess::get_file_as_array(p_so.path); + print_verbose("Copying .so file from " + p_so.path + " to " + dst_path); + Error err = store_file_at_path(dst_path, data); + ERR_FAIL_COND_V_MSG(err, err, "Failed to copy .so file from " + p_so.path + " to " + dst_path); + export_data->libs.push_back(dst_path); + } + } + ERR_FAIL_COND_V_MSG(!exported, FAILED, + "Cannot determine ABI for library \"" + p_so.path + "\". One of the supported ABIs must be used as a tag: " + String(" ").join(abis)); + return OK; +} + +void EditorExportPlatformAndroid::_get_permissions(const Ref<EditorExportPreset> &p_preset, bool p_give_internet, Vector<String> &r_permissions) { + const char **aperms = android_perms; + while (*aperms) { + bool enabled = p_preset->get("permissions/" + String(*aperms).to_lower()); + if (enabled) { + r_permissions.push_back("android.permission." + String(*aperms)); + } + aperms++; + } + PackedStringArray user_perms = p_preset->get("permissions/custom_permissions"); + for (int i = 0; i < user_perms.size(); i++) { + String user_perm = user_perms[i].strip_edges(); + if (!user_perm.is_empty()) { + r_permissions.push_back(user_perm); + } + } + if (p_give_internet) { + if (r_permissions.find("android.permission.INTERNET") == -1) { + r_permissions.push_back("android.permission.INTERNET"); + } + } + + int xr_mode_index = p_preset->get("xr_features/xr_mode"); + if (xr_mode_index == 1 /* XRMode.OVR */) { + int hand_tracking_index = p_preset->get("xr_features/hand_tracking"); // 0: none, 1: optional, 2: required + if (hand_tracking_index > 0) { + if (r_permissions.find("com.oculus.permission.HAND_TRACKING") == -1) { + r_permissions.push_back("com.oculus.permission.HAND_TRACKING"); + } + } + } +} + +void EditorExportPlatformAndroid::_write_tmp_manifest(const Ref<EditorExportPreset> &p_preset, bool p_give_internet, bool p_debug) { + print_verbose("Building temporary manifest.."); + String manifest_text = + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + " xmlns:tools=\"http://schemas.android.com/tools\">\n"; + + manifest_text += _get_screen_sizes_tag(p_preset); + manifest_text += _get_gles_tag(); + + Vector<String> perms; + _get_permissions(p_preset, p_give_internet, perms); + for (int i = 0; i < perms.size(); i++) { + manifest_text += vformat(" <uses-permission android:name=\"%s\" />\n", perms.get(i)); + } + + manifest_text += _get_xr_features_tag(p_preset); + manifest_text += _get_instrumentation_tag(p_preset); + manifest_text += _get_application_tag(p_preset); + manifest_text += "</manifest>\n"; + String manifest_path = vformat("res://android/build/src/%s/AndroidManifest.xml", (p_debug ? "debug" : "release")); + + print_verbose("Storing manifest into " + manifest_path + ": " + "\n" + manifest_text); + store_string_at_path(manifest_path, manifest_text); +} + +void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_manifest, bool p_give_internet) { + // Leaving the unused types commented because looking these constants up + // again later would be annoying + // const int CHUNK_AXML_FILE = 0x00080003; + // const int CHUNK_RESOURCEIDS = 0x00080180; + const int CHUNK_STRINGS = 0x001C0001; + // const int CHUNK_XML_END_NAMESPACE = 0x00100101; + const int CHUNK_XML_END_TAG = 0x00100103; + // const int CHUNK_XML_START_NAMESPACE = 0x00100100; + const int CHUNK_XML_START_TAG = 0x00100102; + // const int CHUNK_XML_TEXT = 0x00100104; + const int UTF8_FLAG = 0x00000100; + + Vector<String> string_table; + + uint32_t ofs = 8; + + uint32_t string_count = 0; + //uint32_t styles_count = 0; + uint32_t string_flags = 0; + uint32_t string_data_offset = 0; + + //uint32_t styles_offset = 0; + uint32_t string_table_begins = 0; + uint32_t string_table_ends = 0; + Vector<uint8_t> stable_extra; + + String version_name = p_preset->get("version/name"); + int version_code = p_preset->get("version/code"); + String package_name = p_preset->get("package/unique_name"); + + const int screen_orientation = + _get_android_orientation_value(DisplayServer::ScreenOrientation(int(GLOBAL_GET("display/window/handheld/orientation")))); + + bool screen_support_small = p_preset->get("screen/support_small"); + bool screen_support_normal = p_preset->get("screen/support_normal"); + bool screen_support_large = p_preset->get("screen/support_large"); + bool screen_support_xlarge = p_preset->get("screen/support_xlarge"); + + int xr_mode_index = p_preset->get("xr_features/xr_mode"); + + bool backup_allowed = p_preset->get("user_data_backup/allow"); + bool classify_as_game = p_preset->get("package/classify_as_game"); + + Vector<String> perms; + // Write permissions into the perms variable. + _get_permissions(p_preset, p_give_internet, perms); + + while (ofs < (uint32_t)p_manifest.size()) { + uint32_t chunk = decode_uint32(&p_manifest[ofs]); + uint32_t size = decode_uint32(&p_manifest[ofs + 4]); + + switch (chunk) { + case CHUNK_STRINGS: { + int iofs = ofs + 8; + + string_count = decode_uint32(&p_manifest[iofs]); + //styles_count = decode_uint32(&p_manifest[iofs + 4]); + string_flags = decode_uint32(&p_manifest[iofs + 8]); + string_data_offset = decode_uint32(&p_manifest[iofs + 12]); + //styles_offset = decode_uint32(&p_manifest[iofs + 16]); + /* + printf("string count: %i\n",string_count); + printf("flags: %i\n",string_flags); + printf("sdata ofs: %i\n",string_data_offset); + printf("styles ofs: %i\n",styles_offset); + */ + uint32_t st_offset = iofs + 20; + string_table.resize(string_count); + uint32_t string_end = 0; + + string_table_begins = st_offset; + + for (uint32_t i = 0; i < string_count; i++) { + uint32_t string_at = decode_uint32(&p_manifest[st_offset + i * 4]); + string_at += st_offset + string_count * 4; + + ERR_FAIL_COND_MSG(string_flags & UTF8_FLAG, "Unimplemented, can't read UTF-8 string table."); + + if (string_flags & UTF8_FLAG) { + } else { + uint32_t len = decode_uint16(&p_manifest[string_at]); + Vector<char32_t> ucstring; + ucstring.resize(len + 1); + for (uint32_t j = 0; j < len; j++) { + uint16_t c = decode_uint16(&p_manifest[string_at + 2 + 2 * j]); + ucstring.write[j] = c; + } + string_end = MAX(string_at + 2 + 2 * len, string_end); + ucstring.write[len] = 0; + string_table.write[i] = ucstring.ptr(); + } + } + + for (uint32_t i = string_end; i < (ofs + size); i++) { + stable_extra.push_back(p_manifest[i]); + } + + string_table_ends = ofs + size; + + } break; + case CHUNK_XML_START_TAG: { + int iofs = ofs + 8; + uint32_t name = decode_uint32(&p_manifest[iofs + 12]); + + String tname = string_table[name]; + uint32_t attrcount = decode_uint32(&p_manifest[iofs + 20]); + iofs += 28; + + for (uint32_t i = 0; i < attrcount; i++) { + uint32_t attr_nspace = decode_uint32(&p_manifest[iofs]); + uint32_t attr_name = decode_uint32(&p_manifest[iofs + 4]); + uint32_t attr_value = decode_uint32(&p_manifest[iofs + 8]); + uint32_t attr_resid = decode_uint32(&p_manifest[iofs + 16]); + + const String value = (attr_value != 0xFFFFFFFF) ? string_table[attr_value] : "Res #" + itos(attr_resid); + String attrname = string_table[attr_name]; + const String nspace = (attr_nspace != 0xFFFFFFFF) ? string_table[attr_nspace] : ""; + + //replace project information + if (tname == "manifest" && attrname == "package") { + string_table.write[attr_value] = get_package_name(package_name); + } + + if (tname == "manifest" && attrname == "versionCode") { + encode_uint32(version_code, &p_manifest.write[iofs + 16]); + } + + if (tname == "manifest" && attrname == "versionName") { + if (attr_value == 0xFFFFFFFF) { + WARN_PRINT("Version name in a resource, should be plain text"); + } else { + string_table.write[attr_value] = version_name; + } + } + + if (tname == "application" && attrname == "allowBackup") { + encode_uint32(backup_allowed, &p_manifest.write[iofs + 16]); + } + + if (tname == "application" && attrname == "isGame") { + encode_uint32(classify_as_game, &p_manifest.write[iofs + 16]); + } + + if (tname == "instrumentation" && attrname == "targetPackage") { + string_table.write[attr_value] = get_package_name(package_name); + } + + if (tname == "activity" && attrname == "screenOrientation") { + encode_uint32(screen_orientation, &p_manifest.write[iofs + 16]); + } + + if (tname == "supports-screens") { + if (attrname == "smallScreens") { + encode_uint32(screen_support_small ? 0xFFFFFFFF : 0, &p_manifest.write[iofs + 16]); + + } else if (attrname == "normalScreens") { + encode_uint32(screen_support_normal ? 0xFFFFFFFF : 0, &p_manifest.write[iofs + 16]); + + } else if (attrname == "largeScreens") { + encode_uint32(screen_support_large ? 0xFFFFFFFF : 0, &p_manifest.write[iofs + 16]); + + } else if (attrname == "xlargeScreens") { + encode_uint32(screen_support_xlarge ? 0xFFFFFFFF : 0, &p_manifest.write[iofs + 16]); + } + } + + iofs += 20; + } + + } break; + case CHUNK_XML_END_TAG: { + int iofs = ofs + 8; + uint32_t name = decode_uint32(&p_manifest[iofs + 12]); + String tname = string_table[name]; + + if (tname == "uses-feature") { + Vector<String> feature_names; + Vector<bool> feature_required_list; + Vector<int> feature_versions; + + if (xr_mode_index == 1 /* XRMode.OVR */) { + // Check for hand tracking + int hand_tracking_index = p_preset->get("xr_features/hand_tracking"); // 0: none, 1: optional, 2: required + if (hand_tracking_index > 0) { + feature_names.push_back("oculus.software.handtracking"); + feature_required_list.push_back(hand_tracking_index == 2); + feature_versions.push_back(-1); // no version attribute should be added. + } + } + + if (feature_names.size() > 0) { + ofs += 24; // skip over end tag + + // save manifest ending so we can restore it + Vector<uint8_t> manifest_end; + uint32_t manifest_cur_size = p_manifest.size(); + + manifest_end.resize(p_manifest.size() - ofs); + memcpy(manifest_end.ptrw(), &p_manifest[ofs], manifest_end.size()); + + int32_t attr_name_string = string_table.find("name"); + ERR_FAIL_COND_MSG(attr_name_string == -1, "Template does not have 'name' attribute."); + + int32_t ns_android_string = string_table.find("http://schemas.android.com/apk/res/android"); + if (ns_android_string == -1) { + string_table.push_back("http://schemas.android.com/apk/res/android"); + ns_android_string = string_table.size() - 1; + } + + int32_t attr_uses_feature_string = string_table.find("uses-feature"); + if (attr_uses_feature_string == -1) { + string_table.push_back("uses-feature"); + attr_uses_feature_string = string_table.size() - 1; + } + + int32_t attr_required_string = string_table.find("required"); + if (attr_required_string == -1) { + string_table.push_back("required"); + attr_required_string = string_table.size() - 1; + } + + for (int i = 0; i < feature_names.size(); i++) { + String feature_name = feature_names[i]; + bool feature_required = feature_required_list[i]; + int feature_version = feature_versions[i]; + bool has_version_attribute = feature_version != -1; + + print_line("Adding feature " + feature_name); + + int32_t feature_string = string_table.find(feature_name); + if (feature_string == -1) { + string_table.push_back(feature_name); + feature_string = string_table.size() - 1; + } + + String required_value_string = feature_required ? "true" : "false"; + int32_t required_value = string_table.find(required_value_string); + if (required_value == -1) { + string_table.push_back(required_value_string); + required_value = string_table.size() - 1; + } + + int32_t attr_version_string = -1; + int32_t version_value = -1; + int tag_size; + int attr_count; + if (has_version_attribute) { + attr_version_string = string_table.find("version"); + if (attr_version_string == -1) { + string_table.push_back("version"); + attr_version_string = string_table.size() - 1; + } + + version_value = string_table.find(itos(feature_version)); + if (version_value == -1) { + string_table.push_back(itos(feature_version)); + version_value = string_table.size() - 1; + } + + tag_size = 96; // node and three attrs + end node + attr_count = 3; + } else { + tag_size = 76; // node and two attrs + end node + attr_count = 2; + } + manifest_cur_size += tag_size + 24; + p_manifest.resize(manifest_cur_size); + + // start tag + encode_uint16(0x102, &p_manifest.write[ofs]); // type + encode_uint16(16, &p_manifest.write[ofs + 2]); // headersize + encode_uint32(tag_size, &p_manifest.write[ofs + 4]); // size + encode_uint32(0, &p_manifest.write[ofs + 8]); // lineno + encode_uint32(-1, &p_manifest.write[ofs + 12]); // comment + encode_uint32(-1, &p_manifest.write[ofs + 16]); // ns + encode_uint32(attr_uses_feature_string, &p_manifest.write[ofs + 20]); // name + encode_uint16(20, &p_manifest.write[ofs + 24]); // attr_start + encode_uint16(20, &p_manifest.write[ofs + 26]); // attr_size + encode_uint16(attr_count, &p_manifest.write[ofs + 28]); // num_attrs + encode_uint16(0, &p_manifest.write[ofs + 30]); // id_index + encode_uint16(0, &p_manifest.write[ofs + 32]); // class_index + encode_uint16(0, &p_manifest.write[ofs + 34]); // style_index + + // android:name attribute + encode_uint32(ns_android_string, &p_manifest.write[ofs + 36]); // ns + encode_uint32(attr_name_string, &p_manifest.write[ofs + 40]); // 'name' + encode_uint32(feature_string, &p_manifest.write[ofs + 44]); // raw_value + encode_uint16(8, &p_manifest.write[ofs + 48]); // typedvalue_size + p_manifest.write[ofs + 50] = 0; // typedvalue_always0 + p_manifest.write[ofs + 51] = 0x03; // typedvalue_type (string) + encode_uint32(feature_string, &p_manifest.write[ofs + 52]); // typedvalue reference + + // android:required attribute + encode_uint32(ns_android_string, &p_manifest.write[ofs + 56]); // ns + encode_uint32(attr_required_string, &p_manifest.write[ofs + 60]); // 'name' + encode_uint32(required_value, &p_manifest.write[ofs + 64]); // raw_value + encode_uint16(8, &p_manifest.write[ofs + 68]); // typedvalue_size + p_manifest.write[ofs + 70] = 0; // typedvalue_always0 + p_manifest.write[ofs + 71] = 0x03; // typedvalue_type (string) + encode_uint32(required_value, &p_manifest.write[ofs + 72]); // typedvalue reference + + ofs += 76; + + if (has_version_attribute) { + // android:version attribute + encode_uint32(ns_android_string, &p_manifest.write[ofs]); // ns + encode_uint32(attr_version_string, &p_manifest.write[ofs + 4]); // 'name' + encode_uint32(version_value, &p_manifest.write[ofs + 8]); // raw_value + encode_uint16(8, &p_manifest.write[ofs + 12]); // typedvalue_size + p_manifest.write[ofs + 14] = 0; // typedvalue_always0 + p_manifest.write[ofs + 15] = 0x03; // typedvalue_type (string) + encode_uint32(version_value, &p_manifest.write[ofs + 16]); // typedvalue reference + + ofs += 20; + } + + // end tag + encode_uint16(0x103, &p_manifest.write[ofs]); // type + encode_uint16(16, &p_manifest.write[ofs + 2]); // headersize + encode_uint32(24, &p_manifest.write[ofs + 4]); // size + encode_uint32(0, &p_manifest.write[ofs + 8]); // lineno + encode_uint32(-1, &p_manifest.write[ofs + 12]); // comment + encode_uint32(-1, &p_manifest.write[ofs + 16]); // ns + encode_uint32(attr_uses_feature_string, &p_manifest.write[ofs + 20]); // name + + ofs += 24; + } + memcpy(&p_manifest.write[ofs], manifest_end.ptr(), manifest_end.size()); + ofs -= 24; // go back over back end + } + } + if (tname == "manifest") { + // save manifest ending so we can restore it + Vector<uint8_t> manifest_end; + uint32_t manifest_cur_size = p_manifest.size(); + + manifest_end.resize(p_manifest.size() - ofs); + memcpy(manifest_end.ptrw(), &p_manifest[ofs], manifest_end.size()); + + int32_t attr_name_string = string_table.find("name"); + ERR_FAIL_COND_MSG(attr_name_string == -1, "Template does not have 'name' attribute."); + + int32_t ns_android_string = string_table.find("android"); + ERR_FAIL_COND_MSG(ns_android_string == -1, "Template does not have 'android' namespace."); + + int32_t attr_uses_permission_string = string_table.find("uses-permission"); + if (attr_uses_permission_string == -1) { + string_table.push_back("uses-permission"); + attr_uses_permission_string = string_table.size() - 1; + } + + for (int i = 0; i < perms.size(); ++i) { + print_line("Adding permission " + perms[i]); + + manifest_cur_size += 56 + 24; // node + end node + p_manifest.resize(manifest_cur_size); + + // Add permission to the string pool + int32_t perm_string = string_table.find(perms[i]); + if (perm_string == -1) { + string_table.push_back(perms[i]); + perm_string = string_table.size() - 1; + } + + // start tag + encode_uint16(0x102, &p_manifest.write[ofs]); // type + encode_uint16(16, &p_manifest.write[ofs + 2]); // headersize + encode_uint32(56, &p_manifest.write[ofs + 4]); // size + encode_uint32(0, &p_manifest.write[ofs + 8]); // lineno + encode_uint32(-1, &p_manifest.write[ofs + 12]); // comment + encode_uint32(-1, &p_manifest.write[ofs + 16]); // ns + encode_uint32(attr_uses_permission_string, &p_manifest.write[ofs + 20]); // name + encode_uint16(20, &p_manifest.write[ofs + 24]); // attr_start + encode_uint16(20, &p_manifest.write[ofs + 26]); // attr_size + encode_uint16(1, &p_manifest.write[ofs + 28]); // num_attrs + encode_uint16(0, &p_manifest.write[ofs + 30]); // id_index + encode_uint16(0, &p_manifest.write[ofs + 32]); // class_index + encode_uint16(0, &p_manifest.write[ofs + 34]); // style_index + + // attribute + encode_uint32(ns_android_string, &p_manifest.write[ofs + 36]); // ns + encode_uint32(attr_name_string, &p_manifest.write[ofs + 40]); // 'name' + encode_uint32(perm_string, &p_manifest.write[ofs + 44]); // raw_value + encode_uint16(8, &p_manifest.write[ofs + 48]); // typedvalue_size + p_manifest.write[ofs + 50] = 0; // typedvalue_always0 + p_manifest.write[ofs + 51] = 0x03; // typedvalue_type (string) + encode_uint32(perm_string, &p_manifest.write[ofs + 52]); // typedvalue reference + + ofs += 56; + + // end tag + encode_uint16(0x103, &p_manifest.write[ofs]); // type + encode_uint16(16, &p_manifest.write[ofs + 2]); // headersize + encode_uint32(24, &p_manifest.write[ofs + 4]); // size + encode_uint32(0, &p_manifest.write[ofs + 8]); // lineno + encode_uint32(-1, &p_manifest.write[ofs + 12]); // comment + encode_uint32(-1, &p_manifest.write[ofs + 16]); // ns + encode_uint32(attr_uses_permission_string, &p_manifest.write[ofs + 20]); // name + + ofs += 24; + } + + // copy footer back in + memcpy(&p_manifest.write[ofs], manifest_end.ptr(), manifest_end.size()); + } + } break; + } + + ofs += size; + } + + //create new andriodmanifest binary + + Vector<uint8_t> ret; + ret.resize(string_table_begins + string_table.size() * 4); + + for (uint32_t i = 0; i < string_table_begins; i++) { + ret.write[i] = p_manifest[i]; + } + + ofs = 0; + for (int i = 0; i < string_table.size(); i++) { + encode_uint32(ofs, &ret.write[string_table_begins + i * 4]); + ofs += string_table[i].length() * 2 + 2 + 2; + } + + ret.resize(ret.size() + ofs); + string_data_offset = ret.size() - ofs; + uint8_t *chars = &ret.write[string_data_offset]; + for (int i = 0; i < string_table.size(); i++) { + String s = string_table[i]; + encode_uint16(s.length(), chars); + chars += 2; + for (int j = 0; j < s.length(); j++) { + encode_uint16(s[j], chars); + chars += 2; + } + encode_uint16(0, chars); + chars += 2; + } + + for (int i = 0; i < stable_extra.size(); i++) { + ret.push_back(stable_extra[i]); + } + + //pad + while (ret.size() % 4) { + ret.push_back(0); + } + + uint32_t new_stable_end = ret.size(); + + uint32_t extra = (p_manifest.size() - string_table_ends); + ret.resize(new_stable_end + extra); + for (uint32_t i = 0; i < extra; i++) { + ret.write[new_stable_end + i] = p_manifest[string_table_ends + i]; + } + + while (ret.size() % 4) { + ret.push_back(0); + } + encode_uint32(ret.size(), &ret.write[4]); //update new file size + + encode_uint32(new_stable_end - 8, &ret.write[12]); //update new string table size + encode_uint32(string_table.size(), &ret.write[16]); //update new number of strings + encode_uint32(string_data_offset - 8, &ret.write[28]); //update new string data offset + + p_manifest = ret; +} + +String EditorExportPlatformAndroid::_parse_string(const uint8_t *p_bytes, bool p_utf8) { + uint32_t offset = 0; + uint32_t len = 0; + + if (p_utf8) { + uint8_t byte = p_bytes[offset]; + if (byte & 0x80) { + offset += 2; + } else { + offset += 1; + } + byte = p_bytes[offset]; + offset++; + if (byte & 0x80) { + len = byte & 0x7F; + len = (len << 8) + p_bytes[offset]; + offset++; + } else { + len = byte; + } + } else { + len = decode_uint16(&p_bytes[offset]); + offset += 2; + if (len & 0x8000) { + len &= 0x7FFF; + len = (len << 16) + decode_uint16(&p_bytes[offset]); + offset += 2; + } + } + + if (p_utf8) { + Vector<uint8_t> str8; + str8.resize(len + 1); + for (uint32_t i = 0; i < len; i++) { + str8.write[i] = p_bytes[offset + i]; + } + str8.write[len] = 0; + String str; + str.parse_utf8((const char *)str8.ptr()); + return str; + } else { + String str; + for (uint32_t i = 0; i < len; i++) { + char32_t c = decode_uint16(&p_bytes[offset + i * 2]); + if (c == 0) { + break; + } + str += String::chr(c); + } + return str; + } +} + +void EditorExportPlatformAndroid::_fix_resources(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &r_manifest) { + const int UTF8_FLAG = 0x00000100; + + uint32_t string_block_len = decode_uint32(&r_manifest[16]); + uint32_t string_count = decode_uint32(&r_manifest[20]); + uint32_t string_flags = decode_uint32(&r_manifest[28]); + const uint32_t string_table_begins = 40; + + Vector<String> string_table; + + String package_name = p_preset->get("package/name"); + + for (uint32_t i = 0; i < string_count; i++) { + uint32_t offset = decode_uint32(&r_manifest[string_table_begins + i * 4]); + offset += string_table_begins + string_count * 4; + + String str = _parse_string(&r_manifest[offset], string_flags & UTF8_FLAG); + + if (str.begins_with("godot-project-name")) { + if (str == "godot-project-name") { + //project name + str = get_project_name(package_name); + + } else { + String lang = str.substr(str.rfind("-") + 1, str.length()).replace("-", "_"); + String prop = "application/config/name_" + lang; + if (ProjectSettings::get_singleton()->has_setting(prop)) { + str = ProjectSettings::get_singleton()->get(prop); + } else { + str = get_project_name(package_name); + } + } + } + + string_table.push_back(str); + } + + //write a new string table, but use 16 bits + Vector<uint8_t> ret; + ret.resize(string_table_begins + string_table.size() * 4); + + for (uint32_t i = 0; i < string_table_begins; i++) { + ret.write[i] = r_manifest[i]; + } + + int ofs = 0; + for (int i = 0; i < string_table.size(); i++) { + encode_uint32(ofs, &ret.write[string_table_begins + i * 4]); + ofs += string_table[i].length() * 2 + 2 + 2; + } + + ret.resize(ret.size() + ofs); + uint8_t *chars = &ret.write[ret.size() - ofs]; + for (int i = 0; i < string_table.size(); i++) { + String s = string_table[i]; + encode_uint16(s.length(), chars); + chars += 2; + for (int j = 0; j < s.length(); j++) { + encode_uint16(s[j], chars); + chars += 2; + } + encode_uint16(0, chars); + chars += 2; + } + + //pad + while (ret.size() % 4) { + ret.push_back(0); + } + + //change flags to not use utf8 + encode_uint32(string_flags & ~0x100, &ret.write[28]); + //change length + encode_uint32(ret.size() - 12, &ret.write[16]); + //append the rest... + int rest_from = 12 + string_block_len; + int rest_to = ret.size(); + int rest_len = (r_manifest.size() - rest_from); + ret.resize(ret.size() + (r_manifest.size() - rest_from)); + for (int i = 0; i < rest_len; i++) { + ret.write[rest_to + i] = r_manifest[rest_from + i]; + } + //finally update the size + encode_uint32(ret.size(), &ret.write[4]); + + r_manifest = ret; + //printf("end\n"); +} + +void EditorExportPlatformAndroid::_load_image_data(const Ref<Image> &p_splash_image, Vector<uint8_t> &p_data) { + Vector<uint8_t> png_buffer; + Error err = PNGDriverCommon::image_to_png(p_splash_image, png_buffer); + if (err == OK) { + p_data.resize(png_buffer.size()); + memcpy(p_data.ptrw(), png_buffer.ptr(), p_data.size()); + } else { + String err_str = String("Failed to convert splash image to png."); + WARN_PRINT(err_str.utf8().get_data()); + } +} + +void EditorExportPlatformAndroid::_process_launcher_icons(const String &p_file_name, const Ref<Image> &p_source_image, int dimension, Vector<uint8_t> &p_data) { + Ref<Image> working_image = p_source_image; + + if (p_source_image->get_width() != dimension || p_source_image->get_height() != dimension) { + working_image = p_source_image->duplicate(); + working_image->resize(dimension, dimension, Image::Interpolation::INTERPOLATE_LANCZOS); + } + + Vector<uint8_t> png_buffer; + Error err = PNGDriverCommon::image_to_png(working_image, png_buffer); + if (err == OK) { + p_data.resize(png_buffer.size()); + memcpy(p_data.ptrw(), png_buffer.ptr(), p_data.size()); + } else { + String err_str = String("Failed to convert resized icon (") + p_file_name + ") to png."; + WARN_PRINT(err_str.utf8().get_data()); + } +} + +String EditorExportPlatformAndroid::load_splash_refs(Ref<Image> &splash_image, Ref<Image> &splash_bg_color_image) { + bool scale_splash = ProjectSettings::get_singleton()->get("application/boot_splash/fullsize"); + bool apply_filter = ProjectSettings::get_singleton()->get("application/boot_splash/use_filter"); + String project_splash_path = ProjectSettings::get_singleton()->get("application/boot_splash/image"); + + if (!project_splash_path.is_empty()) { + splash_image.instantiate(); + print_verbose("Loading splash image: " + project_splash_path); + const Error err = ImageLoader::load_image(project_splash_path, splash_image); + if (err) { + if (OS::get_singleton()->is_stdout_verbose()) { + print_error("- unable to load splash image from " + project_splash_path + " (" + itos(err) + ")"); + } + splash_image.unref(); + } + } + + if (splash_image.is_null()) { + // Use the default + print_verbose("Using default splash image."); + splash_image = Ref<Image>(memnew(Image(boot_splash_png))); + } + + if (scale_splash) { + Size2 screen_size = Size2(ProjectSettings::get_singleton()->get("display/window/size/width"), ProjectSettings::get_singleton()->get("display/window/size/height")); + int width, height; + if (screen_size.width > screen_size.height) { + // scale horizontally + height = screen_size.height; + width = splash_image->get_width() * screen_size.height / splash_image->get_height(); + } else { + // scale vertically + width = screen_size.width; + height = splash_image->get_height() * screen_size.width / splash_image->get_width(); + } + splash_image->resize(width, height); + } + + // Setup the splash bg color + bool bg_color_valid; + Color bg_color = ProjectSettings::get_singleton()->get("application/boot_splash/bg_color", &bg_color_valid); + if (!bg_color_valid) { + bg_color = boot_splash_bg_color; + } + + print_verbose("Creating splash background color image."); + splash_bg_color_image.instantiate(); + splash_bg_color_image->create(splash_image->get_width(), splash_image->get_height(), false, splash_image->get_format()); + splash_bg_color_image->fill(bg_color); + + String processed_splash_config_xml = vformat(SPLASH_CONFIG_XML_CONTENT, bool_to_string(apply_filter)); + return processed_splash_config_xml; +} + +void EditorExportPlatformAndroid::load_icon_refs(const Ref<EditorExportPreset> &p_preset, Ref<Image> &icon, Ref<Image> &foreground, Ref<Image> &background) { + String project_icon_path = ProjectSettings::get_singleton()->get("application/config/icon"); + + icon.instantiate(); + foreground.instantiate(); + background.instantiate(); + + // Regular icon: user selection -> project icon -> default. + String path = static_cast<String>(p_preset->get(launcher_icon_option)).strip_edges(); + print_verbose("Loading regular icon from " + path); + if (path.is_empty() || ImageLoader::load_image(path, icon) != OK) { + print_verbose("- falling back to project icon: " + project_icon_path); + ImageLoader::load_image(project_icon_path, icon); + } + + // Adaptive foreground: user selection -> regular icon (user selection -> project icon -> default). + path = static_cast<String>(p_preset->get(launcher_adaptive_icon_foreground_option)).strip_edges(); + print_verbose("Loading adaptive foreground icon from " + path); + if (path.is_empty() || ImageLoader::load_image(path, foreground) != OK) { + print_verbose("- falling back to using the regular icon"); + foreground = icon; + } + + // Adaptive background: user selection -> default. + path = static_cast<String>(p_preset->get(launcher_adaptive_icon_background_option)).strip_edges(); + if (!path.is_empty()) { + print_verbose("Loading adaptive background icon from " + path); + ImageLoader::load_image(path, background); + } +} + +void EditorExportPlatformAndroid::store_image(const LauncherIcon launcher_icon, const Vector<uint8_t> &data) { + store_image(launcher_icon.export_path, data); +} + +void EditorExportPlatformAndroid::store_image(const String &export_path, const Vector<uint8_t> &data) { + String img_path = export_path.insert(0, "res://android/build/"); + store_file_at_path(img_path, data); +} + +void EditorExportPlatformAndroid::_copy_icons_to_gradle_project(const Ref<EditorExportPreset> &p_preset, + const String &processed_splash_config_xml, + const Ref<Image> &splash_image, + const Ref<Image> &splash_bg_color_image, + const Ref<Image> &main_image, + const Ref<Image> &foreground, + const Ref<Image> &background) { + // Store the splash configuration + if (!processed_splash_config_xml.is_empty()) { + print_verbose("Storing processed splash configuration: " + String("\n") + processed_splash_config_xml); + store_string_at_path(SPLASH_CONFIG_PATH, processed_splash_config_xml); + } + + // Store the splash image + if (splash_image.is_valid() && !splash_image->is_empty()) { + print_verbose("Storing splash image in " + String(SPLASH_IMAGE_EXPORT_PATH)); + Vector<uint8_t> data; + _load_image_data(splash_image, data); + store_image(SPLASH_IMAGE_EXPORT_PATH, data); + } + + // Store the splash bg color image + if (splash_bg_color_image.is_valid() && !splash_bg_color_image->is_empty()) { + print_verbose("Storing splash background image in " + String(SPLASH_BG_COLOR_PATH)); + Vector<uint8_t> data; + _load_image_data(splash_bg_color_image, data); + store_image(SPLASH_BG_COLOR_PATH, data); + } + + // Prepare images to be resized for the icons. If some image ends up being uninitialized, + // the default image from the export template will be used. + + for (int i = 0; i < icon_densities_count; ++i) { + if (main_image.is_valid() && !main_image->is_empty()) { + print_verbose("Processing launcher icon for dimension " + itos(launcher_icons[i].dimensions) + " into " + launcher_icons[i].export_path); + Vector<uint8_t> data; + _process_launcher_icons(launcher_icons[i].export_path, main_image, launcher_icons[i].dimensions, data); + store_image(launcher_icons[i], data); + } + + if (foreground.is_valid() && !foreground->is_empty()) { + print_verbose("Processing launcher adaptive icon foreground for dimension " + itos(launcher_adaptive_icon_foregrounds[i].dimensions) + " into " + launcher_adaptive_icon_foregrounds[i].export_path); + Vector<uint8_t> data; + _process_launcher_icons(launcher_adaptive_icon_foregrounds[i].export_path, foreground, + launcher_adaptive_icon_foregrounds[i].dimensions, data); + store_image(launcher_adaptive_icon_foregrounds[i], data); + } + + if (background.is_valid() && !background->is_empty()) { + print_verbose("Processing launcher adaptive icon background for dimension " + itos(launcher_adaptive_icon_backgrounds[i].dimensions) + " into " + launcher_adaptive_icon_backgrounds[i].export_path); + Vector<uint8_t> data; + _process_launcher_icons(launcher_adaptive_icon_backgrounds[i].export_path, background, + launcher_adaptive_icon_backgrounds[i].dimensions, data); + store_image(launcher_adaptive_icon_backgrounds[i], data); + } + } +} + +Vector<String> EditorExportPlatformAndroid::get_enabled_abis(const Ref<EditorExportPreset> &p_preset) { + Vector<String> abis = get_abis(); + Vector<String> enabled_abis; + for (int i = 0; i < abis.size(); ++i) { + bool is_enabled = p_preset->get("architectures/" + abis[i]); + if (is_enabled) { + enabled_abis.push_back(abis[i]); + } + } + return enabled_abis; +} + +void EditorExportPlatformAndroid::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) { + String driver = ProjectSettings::get_singleton()->get("rendering/driver/driver_name"); + if (driver == "GLES2") { + r_features->push_back("etc"); + } + // FIXME: Review what texture formats are used for Vulkan. + if (driver == "Vulkan") { + r_features->push_back("etc2"); + } + + Vector<String> abis = get_enabled_abis(p_preset); + for (int i = 0; i < abis.size(); ++i) { + r_features->push_back(abis[i]); + } +} + +void EditorExportPlatformAndroid::get_export_options(List<ExportOption> *r_options) { + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.apk"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.apk"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "custom_template/use_custom_build"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "custom_template/export_format", PROPERTY_HINT_ENUM, "Export APK,Export AAB"), EXPORT_FORMAT_APK)); + + Vector<PluginConfigAndroid> plugins_configs = get_plugins(); + for (int i = 0; i < plugins_configs.size(); i++) { + print_verbose("Found Android plugin " + plugins_configs[i].name); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "plugins/" + plugins_configs[i].name), false)); + } + plugins_changed.clear(); + + Vector<String> abis = get_abis(); + for (int i = 0; i < abis.size(); ++i) { + String abi = abis[i]; + bool is_default = (abi == "armeabi-v7a" || abi == "arm64-v8a"); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "architectures/" + abi), is_default)); + } + + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug", PROPERTY_HINT_GLOBAL_FILE, "*.keystore,*.jks"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug_user"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug_password"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release", PROPERTY_HINT_GLOBAL_FILE, "*.keystore,*.jks"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_user"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_password"), "")); + + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "one_click_deploy/clear_previous_install"), false)); + + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/code", PROPERTY_HINT_RANGE, "1,4096,1,or_greater"), 1)); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "version/name"), "1.0")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/unique_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "ext.domain.name"), "org.godotengine.$genname")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name [default if blank]"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/signed"), true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/classify_as_game"), true)); + + 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::BOOL, "graphics/32_bits_framebuffer"), true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "graphics/opengl_debug"), false)); + + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/xr_mode", PROPERTY_HINT_ENUM, "Regular,Oculus Mobile VR"), 0)); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "xr_features/hand_tracking", PROPERTY_HINT_ENUM, "None,Optional,Required"), 0)); + + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/immersive_mode"), true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_small"), true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_normal"), true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_large"), true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_xlarge"), true)); + + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "user_data_backup/allow"), false)); + + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "command_line/extra_args"), "")); + + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "apk_expansion/enable"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "apk_expansion/SALT"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "apk_expansion/public_key", PROPERTY_HINT_MULTILINE_TEXT), "")); + + r_options->push_back(ExportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "permissions/custom_permissions"), PackedStringArray())); + + const char **perms = android_perms; + while (*perms) { + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "permissions/" + String(*perms).to_lower()), false)); + perms++; + } +} + +String EditorExportPlatformAndroid::get_name() const { + return "Android"; +} + +String EditorExportPlatformAndroid::get_os_name() const { + return "Android"; +} + +Ref<Texture2D> EditorExportPlatformAndroid::get_logo() const { + return logo; +} + +bool EditorExportPlatformAndroid::should_update_export_options() { + bool export_options_changed = plugins_changed.is_set(); + if (export_options_changed) { + // don't clear unless we're reporting true, to avoid race + plugins_changed.clear(); + } + return export_options_changed; +} + +bool EditorExportPlatformAndroid::poll_export() { + bool dc = devices_changed.is_set(); + if (dc) { + // don't clear unless we're reporting true, to avoid race + devices_changed.clear(); + } + return dc; +} + +int EditorExportPlatformAndroid::get_options_count() const { + MutexLock lock(device_lock); + return devices.size(); +} + +String EditorExportPlatformAndroid::get_options_tooltip() const { + return TTR("Select device from the list"); +} + +String EditorExportPlatformAndroid::get_option_label(int p_index) const { + ERR_FAIL_INDEX_V(p_index, devices.size(), ""); + MutexLock lock(device_lock); + return devices[p_index].name; +} + +String EditorExportPlatformAndroid::get_option_tooltip(int p_index) const { + ERR_FAIL_INDEX_V(p_index, devices.size(), ""); + MutexLock lock(device_lock); + String s = devices[p_index].description; + if (devices.size() == 1) { + // Tooltip will be: + // Name + // Description + s = devices[p_index].name + "\n\n" + s; + } + return s; +} + +Error EditorExportPlatformAndroid::run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) { + ERR_FAIL_INDEX_V(p_device, devices.size(), ERR_INVALID_PARAMETER); + + String can_export_error; + bool can_export_missing_templates; + if (!can_export(p_preset, can_export_error, can_export_missing_templates)) { + EditorNode::add_io_error(can_export_error); + return ERR_UNCONFIGURED; + } + + MutexLock lock(device_lock); + + EditorProgress ep("run", vformat(TTR("Running on %s"), devices[p_device].name), 3); + + String adb = get_adb_path(); + + // Export_temp APK. + if (ep.step(TTR("Exporting APK..."), 0)) { + return ERR_SKIP; + } + + const bool use_remote = (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) || (p_debug_flags & DEBUG_FLAG_DUMB_CLIENT); + const bool use_reverse = devices[p_device].api_level >= 21; + + if (use_reverse) { + p_debug_flags |= DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST; + } + + String tmp_export_path = EditorPaths::get_singleton()->get_cache_dir().plus_file("tmpexport." + uitos(OS::get_singleton()->get_unix_time()) + ".apk"); + +#define CLEANUP_AND_RETURN(m_err) \ + { \ + DirAccess::remove_file_or_error(tmp_export_path); \ + return m_err; \ + } + + // Export to temporary APK before sending to device. + Error err = export_project_helper(p_preset, true, tmp_export_path, EXPORT_FORMAT_APK, true, p_debug_flags); + + if (err != OK) { + CLEANUP_AND_RETURN(err); + } + + List<String> args; + int rv; + String output; + + bool remove_prev = p_preset->get("one_click_deploy/clear_previous_install"); + String version_name = p_preset->get("version/name"); + String package_name = p_preset->get("package/unique_name"); + + if (remove_prev) { + if (ep.step(TTR("Uninstalling..."), 1)) { + CLEANUP_AND_RETURN(ERR_SKIP); + } + + print_line("Uninstalling previous version: " + devices[p_device].name); + + args.push_back("-s"); + args.push_back(devices[p_device].id); + args.push_back("uninstall"); + args.push_back(get_package_name(package_name)); + + output.clear(); + err = OS::get_singleton()->execute(adb, args, &output, &rv, true); + print_verbose(output); + } + + print_line("Installing to device (please wait...): " + devices[p_device].name); + if (ep.step(TTR("Installing to device, please wait..."), 2)) { + CLEANUP_AND_RETURN(ERR_SKIP); + } + + args.clear(); + args.push_back("-s"); + args.push_back(devices[p_device].id); + args.push_back("install"); + args.push_back("-r"); + args.push_back(tmp_export_path); + + output.clear(); + err = OS::get_singleton()->execute(adb, args, &output, &rv, true); + print_verbose(output); + if (err || rv != 0) { + EditorNode::add_io_error(vformat(TTR("Could not install to device: %s"), output)); + CLEANUP_AND_RETURN(ERR_CANT_CREATE); + } + + if (use_remote) { + if (use_reverse) { + static const char *const msg = "--- Device API >= 21; debugging over USB ---"; + EditorNode::get_singleton()->get_log()->add_message(msg, EditorLog::MSG_TYPE_EDITOR); + print_line(String(msg).to_upper()); + + args.clear(); + args.push_back("-s"); + args.push_back(devices[p_device].id); + args.push_back("reverse"); + args.push_back("--remove-all"); + output.clear(); + OS::get_singleton()->execute(adb, args, &output, &rv, true); + print_verbose(output); + + if (p_debug_flags & DEBUG_FLAG_REMOTE_DEBUG) { + int dbg_port = EditorSettings::get_singleton()->get("network/debug/remote_port"); + args.clear(); + args.push_back("-s"); + args.push_back(devices[p_device].id); + args.push_back("reverse"); + args.push_back("tcp:" + itos(dbg_port)); + args.push_back("tcp:" + itos(dbg_port)); + + output.clear(); + OS::get_singleton()->execute(adb, args, &output, &rv, true); + print_verbose(output); + print_line("Reverse result: " + itos(rv)); + } + + if (p_debug_flags & DEBUG_FLAG_DUMB_CLIENT) { + int fs_port = EditorSettings::get_singleton()->get("filesystem/file_server/port"); + + args.clear(); + args.push_back("-s"); + args.push_back(devices[p_device].id); + args.push_back("reverse"); + args.push_back("tcp:" + itos(fs_port)); + args.push_back("tcp:" + itos(fs_port)); + + output.clear(); + err = OS::get_singleton()->execute(adb, args, &output, &rv, true); + print_verbose(output); + print_line("Reverse result2: " + itos(rv)); + } + } else { + static const char *const msg = "--- Device API < 21; debugging over Wi-Fi ---"; + EditorNode::get_singleton()->get_log()->add_message(msg, EditorLog::MSG_TYPE_EDITOR); + print_line(String(msg).to_upper()); + } + } + + if (ep.step(TTR("Running on device..."), 3)) { + CLEANUP_AND_RETURN(ERR_SKIP); + } + args.clear(); + args.push_back("-s"); + args.push_back(devices[p_device].id); + args.push_back("shell"); + args.push_back("am"); + args.push_back("start"); + if ((bool)EditorSettings::get_singleton()->get("export/android/force_system_user") && devices[p_device].api_level >= 17) { // Multi-user introduced in Android 17 + args.push_back("--user"); + args.push_back("0"); + } + args.push_back("-a"); + args.push_back("android.intent.action.MAIN"); + args.push_back("-n"); + args.push_back(get_package_name(package_name) + "/com.godot.game.GodotApp"); + + output.clear(); + err = OS::get_singleton()->execute(adb, args, &output, &rv, true); + print_verbose(output); + if (err || rv != 0) { + EditorNode::add_io_error(TTR("Could not execute on device.")); + CLEANUP_AND_RETURN(ERR_CANT_CREATE); + } + + CLEANUP_AND_RETURN(OK); +#undef CLEANUP_AND_RETURN +} + +Ref<Texture2D> EditorExportPlatformAndroid::get_run_icon() const { + return run_icon; +} + +String EditorExportPlatformAndroid::get_adb_path() { + String exe_ext = ""; + if (OS::get_singleton()->get_name() == "Windows") { + exe_ext = ".exe"; + } + String sdk_path = EditorSettings::get_singleton()->get("export/android/android_sdk_path"); + return sdk_path.plus_file("platform-tools/adb" + exe_ext); +} + +String EditorExportPlatformAndroid::get_apksigner_path() { + String exe_ext = ""; + if (OS::get_singleton()->get_name() == "Windows") { + exe_ext = ".bat"; + } + String apksigner_command_name = "apksigner" + exe_ext; + String sdk_path = EditorSettings::get_singleton()->get("export/android/android_sdk_path"); + String apksigner_path = ""; + + Error errn; + String build_tools_dir = sdk_path.plus_file("build-tools"); + DirAccessRef da = DirAccess::open(build_tools_dir, &errn); + if (errn != OK) { + print_error("Unable to open Android 'build-tools' directory."); + return apksigner_path; + } + + // There are additional versions directories we need to go through. + da->list_dir_begin(); + String sub_dir = da->get_next(); + while (!sub_dir.is_empty()) { + if (!sub_dir.begins_with(".") && da->current_is_dir()) { + // Check if the tool is here. + String tool_path = build_tools_dir.plus_file(sub_dir).plus_file(apksigner_command_name); + if (FileAccess::exists(tool_path)) { + apksigner_path = tool_path; + break; + } + } + sub_dir = da->get_next(); + } + da->list_dir_end(); + + if (apksigner_path.is_empty()) { + EditorNode::get_singleton()->show_warning(TTR("Unable to find the 'apksigner' tool.")); + } + + return apksigner_path; +} + +bool EditorExportPlatformAndroid::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const { + String err; + bool valid = false; + + // Look for export templates (first official, and if defined custom templates). + + if (!bool(p_preset->get("custom_template/use_custom_build"))) { + String template_err; + bool dvalid = false; + bool rvalid = false; + bool has_export_templates = false; + + if (p_preset->get("custom_template/debug") != "") { + dvalid = FileAccess::exists(p_preset->get("custom_template/debug")); + if (!dvalid) { + template_err += TTR("Custom debug template not found.") + "\n"; + } + } else { + has_export_templates |= exists_export_template("android_debug.apk", &template_err); + } + + if (p_preset->get("custom_template/release") != "") { + rvalid = FileAccess::exists(p_preset->get("custom_template/release")); + if (!rvalid) { + template_err += TTR("Custom release template not found.") + "\n"; + } + } else { + has_export_templates |= exists_export_template("android_release.apk", &template_err); + } + + r_missing_templates = !has_export_templates; + valid = dvalid || rvalid || has_export_templates; + if (!valid) { + err += template_err; + } + } else { + bool installed_android_build_template = FileAccess::exists("res://android/build/build.gradle"); + if (!installed_android_build_template) { + r_missing_templates = !exists_export_template("android_source.zip", &err); + err += TTR("Android build template not installed in the project. Install it from the Project menu.") + "\n"; + } else { + r_missing_templates = false; + } + + valid = installed_android_build_template && !r_missing_templates; + } + + // Validate the rest of the configuration. + + String dk = p_preset->get("keystore/debug"); + String dk_user = p_preset->get("keystore/debug_user"); + String dk_password = p_preset->get("keystore/debug_password"); + + if ((dk.is_empty() || dk_user.is_empty() || dk_password.is_empty()) && (!dk.is_empty() || !dk_user.is_empty() || !dk_password.is_empty())) { + valid = false; + err += TTR("Either Debug Keystore, Debug User AND Debug Password settings must be configured OR none of them.") + "\n"; + } + + if (!FileAccess::exists(dk)) { + dk = EditorSettings::get_singleton()->get("export/android/debug_keystore"); + if (!FileAccess::exists(dk)) { + valid = false; + err += TTR("Debug keystore not configured in the Editor Settings nor in the preset.") + "\n"; + } + } + + String rk = p_preset->get("keystore/release"); + String rk_user = p_preset->get("keystore/release_user"); + String rk_password = p_preset->get("keystore/release_password"); + + if ((rk.is_empty() || rk_user.is_empty() || rk_password.is_empty()) && (!rk.is_empty() || !rk_user.is_empty() || !rk_password.is_empty())) { + valid = false; + err += TTR("Either Release Keystore, Release User AND Release Password settings must be configured OR none of them.") + "\n"; + } + + if (!rk.is_empty() && !FileAccess::exists(rk)) { + valid = false; + err += TTR("Release keystore incorrectly configured in the export preset.") + "\n"; + } + + String sdk_path = EditorSettings::get_singleton()->get("export/android/android_sdk_path"); + if (sdk_path == "") { + err += TTR("A valid Android SDK path is required in Editor Settings.") + "\n"; + valid = false; + } else { + Error errn; + // Check for the platform-tools directory. + DirAccessRef da = DirAccess::open(sdk_path.plus_file("platform-tools"), &errn); + if (errn != OK) { + err += TTR("Invalid Android SDK path in Editor Settings."); + err += TTR("Missing 'platform-tools' directory!"); + err += "\n"; + valid = false; + } + + // Validate that adb is available + String adb_path = get_adb_path(); + if (!FileAccess::exists(adb_path)) { + err += TTR("Unable to find Android SDK platform-tools' adb command."); + err += TTR("Please check in the Android SDK directory specified in Editor Settings."); + err += "\n"; + valid = false; + } + + // Check for the build-tools directory. + DirAccessRef build_tools_da = DirAccess::open(sdk_path.plus_file("build-tools"), &errn); + if (errn != OK) { + err += TTR("Invalid Android SDK path in Editor Settings."); + err += TTR("Missing 'build-tools' directory!"); + err += "\n"; + valid = false; + } + + // Validate that apksigner is available + String apksigner_path = get_apksigner_path(); + if (!FileAccess::exists(apksigner_path)) { + err += TTR("Unable to find Android SDK build-tools' apksigner command."); + err += TTR("Please check in the Android SDK directory specified in Editor Settings."); + err += "\n"; + valid = false; + } + } + + bool apk_expansion = p_preset->get("apk_expansion/enable"); + + if (apk_expansion) { + String apk_expansion_pkey = p_preset->get("apk_expansion/public_key"); + + if (apk_expansion_pkey == "") { + valid = false; + + err += TTR("Invalid public key for APK expansion.") + "\n"; + } + } + + String pn = p_preset->get("package/unique_name"); + String pn_err; + + if (!is_package_name_valid(get_package_name(pn), &pn_err)) { + valid = false; + err += TTR("Invalid package name:") + " " + pn_err + "\n"; + } + + String etc_error = test_etc2(); + if (etc_error != String()) { + valid = false; + err += etc_error; + } + + // Ensure that `Use Custom Build` is enabled if a plugin is selected. + String enabled_plugins_names = PluginConfigAndroid::get_plugins_names(get_enabled_plugins(p_preset)); + bool custom_build_enabled = p_preset->get("custom_template/use_custom_build"); + if (!enabled_plugins_names.is_empty() && !custom_build_enabled) { + valid = false; + err += TTR("\"Use Custom Build\" must be enabled to use the plugins."); + err += "\n"; + } + + // Validate the Xr features are properly populated + int xr_mode_index = p_preset->get("xr_features/xr_mode"); + int hand_tracking = p_preset->get("xr_features/hand_tracking"); + if (xr_mode_index != /* XRMode.OVR*/ 1) { + if (hand_tracking > 0) { + valid = false; + err += TTR("\"Hand Tracking\" is only valid when \"Xr Mode\" is \"Oculus Mobile VR\"."); + err += "\n"; + } + } + + if (int(p_preset->get("custom_template/export_format")) == EXPORT_FORMAT_AAB && + !bool(p_preset->get("custom_template/use_custom_build"))) { + valid = false; + err += TTR("\"Export AAB\" is only valid when \"Use Custom Build\" is enabled."); + err += "\n"; + } + + r_error = err; + return valid; +} + +List<String> EditorExportPlatformAndroid::get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const { + List<String> list; + list.push_back("apk"); + list.push_back("aab"); + return list; +} + +String EditorExportPlatformAndroid::get_apk_expansion_fullpath(const Ref<EditorExportPreset> &p_preset, const String &p_path) { + int version_code = p_preset->get("version/code"); + String package_name = p_preset->get("package/unique_name"); + String apk_file_name = "main." + itos(version_code) + "." + get_package_name(package_name) + ".obb"; + String fullpath = p_path.get_base_dir().plus_file(apk_file_name); + return fullpath; +} + +Error EditorExportPlatformAndroid::save_apk_expansion_file(const Ref<EditorExportPreset> &p_preset, const String &p_path) { + String fullpath = get_apk_expansion_fullpath(p_preset, p_path); + Error err = save_pack(p_preset, fullpath); + return err; +} + +void EditorExportPlatformAndroid::get_command_line_flags(const Ref<EditorExportPreset> &p_preset, const String &p_path, int p_flags, Vector<uint8_t> &r_command_line_flags) { + String cmdline = p_preset->get("command_line/extra_args"); + Vector<String> command_line_strings = cmdline.strip_edges().split(" "); + for (int i = 0; i < command_line_strings.size(); i++) { + if (command_line_strings[i].strip_edges().length() == 0) { + command_line_strings.remove(i); + i--; + } + } + + gen_export_flags(command_line_strings, p_flags); + + bool apk_expansion = p_preset->get("apk_expansion/enable"); + if (apk_expansion) { + String fullpath = get_apk_expansion_fullpath(p_preset, p_path); + String apk_expansion_public_key = p_preset->get("apk_expansion/public_key"); + + command_line_strings.push_back("--use_apk_expansion"); + command_line_strings.push_back("--apk_expansion_md5"); + command_line_strings.push_back(FileAccess::get_md5(fullpath)); + command_line_strings.push_back("--apk_expansion_key"); + command_line_strings.push_back(apk_expansion_public_key.strip_edges()); + } + + int xr_mode_index = p_preset->get("xr_features/xr_mode"); + if (xr_mode_index == 1) { + command_line_strings.push_back("--xr_mode_ovr"); + } else { // XRMode.REGULAR is the default. + command_line_strings.push_back("--xr_mode_regular"); + } + + bool use_32_bit_framebuffer = p_preset->get("graphics/32_bits_framebuffer"); + if (use_32_bit_framebuffer) { + command_line_strings.push_back("--use_depth_32"); + } + + bool immersive = p_preset->get("screen/immersive_mode"); + if (immersive) { + command_line_strings.push_back("--use_immersive"); + } + + bool debug_opengl = p_preset->get("graphics/opengl_debug"); + if (debug_opengl) { + command_line_strings.push_back("--debug_opengl"); + } + + if (command_line_strings.size()) { + r_command_line_flags.resize(4); + encode_uint32(command_line_strings.size(), &r_command_line_flags.write[0]); + for (int i = 0; i < command_line_strings.size(); i++) { + print_line(itos(i) + " param: " + command_line_strings[i]); + CharString command_line_argument = command_line_strings[i].utf8(); + int base = r_command_line_flags.size(); + int length = command_line_argument.length(); + if (length == 0) { + continue; + } + r_command_line_flags.resize(base + 4 + length); + encode_uint32(length, &r_command_line_flags.write[base]); + memcpy(&r_command_line_flags.write[base + 4], command_line_argument.ptr(), length); + } + } +} + +Error EditorExportPlatformAndroid::sign_apk(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &export_path, EditorProgress &ep) { + int export_format = int(p_preset->get("custom_template/export_format")); + String export_label = export_format == EXPORT_FORMAT_AAB ? "AAB" : "APK"; + String release_keystore = p_preset->get("keystore/release"); + String release_username = p_preset->get("keystore/release_user"); + String release_password = p_preset->get("keystore/release_password"); + + String apksigner = get_apksigner_path(); + print_verbose("Starting signing of the " + export_label + " binary using " + apksigner); + if (!FileAccess::exists(apksigner)) { + EditorNode::add_io_error(vformat(TTR("'apksigner' could not be found.\nPlease check the command is available in the Android SDK build-tools directory.\nThe resulting %s is unsigned."), export_label)); + return OK; + } + + String keystore; + String password; + String user; + if (p_debug) { + keystore = p_preset->get("keystore/debug"); + password = p_preset->get("keystore/debug_password"); + user = p_preset->get("keystore/debug_user"); + + if (keystore.is_empty()) { + keystore = EditorSettings::get_singleton()->get("export/android/debug_keystore"); + password = EditorSettings::get_singleton()->get("export/android/debug_keystore_pass"); + user = EditorSettings::get_singleton()->get("export/android/debug_keystore_user"); + } + + if (ep.step(vformat(TTR("Signing debug %s..."), export_label), 104)) { + return ERR_SKIP; + } + + } else { + keystore = release_keystore; + password = release_password; + user = release_username; + + if (ep.step(vformat(TTR("Signing release %s..."), export_label), 104)) { + return ERR_SKIP; + } + } + + if (!FileAccess::exists(keystore)) { + EditorNode::add_io_error(TTR("Could not find keystore, unable to export.")); + return ERR_FILE_CANT_OPEN; + } + + String output; + List<String> args; + args.push_back("sign"); + args.push_back("--verbose"); + args.push_back("--ks"); + args.push_back(keystore); + args.push_back("--ks-pass"); + args.push_back("pass:" + password); + args.push_back("--ks-key-alias"); + args.push_back(user); + args.push_back(export_path); + if (p_debug) { + // We only print verbose logs for debug builds to avoid leaking release keystore credentials. + print_verbose("Signing debug binary using: " + String("\n") + apksigner + " " + join_list(args, String(" "))); + } + int retval; + output.clear(); + OS::get_singleton()->execute(apksigner, args, &output, &retval, true); + print_verbose(output); + if (retval) { + EditorNode::add_io_error(vformat(TTR("'apksigner' returned with error #%d"), retval)); + return ERR_CANT_CREATE; + } + + if (ep.step(vformat(TTR("Verifying %s..."), export_label), 105)) { + return ERR_SKIP; + } + + args.clear(); + args.push_back("verify"); + args.push_back("--verbose"); + args.push_back(export_path); + if (p_debug) { + print_verbose("Verifying signed build using: " + String("\n") + apksigner + " " + join_list(args, String(" "))); + } + + output.clear(); + OS::get_singleton()->execute(apksigner, args, &output, &retval, true); + print_verbose(output); + if (retval) { + EditorNode::add_io_error(vformat(TTR("'apksigner' verification of %s failed."), export_label)); + return ERR_CANT_CREATE; + } + + print_verbose("Successfully completed signing build."); + return OK; +} + +void EditorExportPlatformAndroid::_clear_assets_directory() { + DirAccessRef da_res = DirAccess::create(DirAccess::ACCESS_RESOURCES); + if (da_res->dir_exists("res://android/build/assets")) { + print_verbose("Clearing assets directory.."); + DirAccessRef da_assets = DirAccess::open("res://android/build/assets"); + da_assets->erase_contents_recursive(); + da_res->remove("res://android/build/assets"); + } +} + +void EditorExportPlatformAndroid::_remove_copied_libs() { + print_verbose("Removing previously installed libraries..."); + Error error; + String libs_json = FileAccess::get_file_as_string(GDNATIVE_LIBS_PATH, &error); + if (error || libs_json.is_empty()) { + print_verbose("No previously installed libraries found"); + return; + } + + JSON json; + error = json.parse(libs_json); + ERR_FAIL_COND_MSG(error, "Error parsing \"" + libs_json + "\" on line " + itos(json.get_error_line()) + ": " + json.get_error_message()); + + Vector<String> libs = json.get_data(); + DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + for (int i = 0; i < libs.size(); i++) { + print_verbose("Removing previously installed library " + libs[i]); + da->remove(libs[i]); + } + da->remove(GDNATIVE_LIBS_PATH); +} + +String EditorExportPlatformAndroid::join_list(List<String> parts, const String &separator) const { + String ret; + for (int i = 0; i < parts.size(); ++i) { + if (i > 0) { + ret += separator; + } + ret += parts[i]; + } + return ret; +} + +Error EditorExportPlatformAndroid::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { + int export_format = int(p_preset->get("custom_template/export_format")); + bool should_sign = p_preset->get("package/signed"); + return export_project_helper(p_preset, p_debug, p_path, export_format, should_sign, p_flags); +} + +Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int export_format, bool should_sign, int p_flags) { + ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags); + + String src_apk; + Error err; + + EditorProgress ep("export", TTR("Exporting for Android"), 105, true); + + bool use_custom_build = bool(p_preset->get("custom_template/use_custom_build")); + bool p_give_internet = p_flags & (DEBUG_FLAG_DUMB_CLIENT | DEBUG_FLAG_REMOTE_DEBUG); + bool apk_expansion = p_preset->get("apk_expansion/enable"); + Vector<String> enabled_abis = get_enabled_abis(p_preset); + + print_verbose("Exporting for Android..."); + print_verbose("- debug build: " + bool_to_string(p_debug)); + print_verbose("- export path: " + p_path); + print_verbose("- export format: " + itos(export_format)); + print_verbose("- sign build: " + bool_to_string(should_sign)); + print_verbose("- custom build enabled: " + bool_to_string(use_custom_build)); + print_verbose("- apk expansion enabled: " + bool_to_string(apk_expansion)); + print_verbose("- enabled abis: " + String(",").join(enabled_abis)); + print_verbose("- export filter: " + itos(p_preset->get_export_filter())); + print_verbose("- include filter: " + p_preset->get_include_filter()); + print_verbose("- exclude filter: " + p_preset->get_exclude_filter()); + + Ref<Image> splash_image; + Ref<Image> splash_bg_color_image; + String processed_splash_config_xml = load_splash_refs(splash_image, splash_bg_color_image); + + Ref<Image> main_image; + Ref<Image> foreground; + Ref<Image> background; + + load_icon_refs(p_preset, main_image, foreground, background); + + Vector<uint8_t> command_line_flags; + // Write command line flags into the command_line_flags variable. + get_command_line_flags(p_preset, p_path, p_flags, command_line_flags); + + if (export_format == EXPORT_FORMAT_AAB) { + if (!p_path.ends_with(".aab")) { + EditorNode::get_singleton()->show_warning(TTR("Invalid filename! Android App Bundle requires the *.aab extension.")); + return ERR_UNCONFIGURED; + } + if (apk_expansion) { + EditorNode::get_singleton()->show_warning(TTR("APK Expansion not compatible with Android App Bundle.")); + return ERR_UNCONFIGURED; + } + } + if (export_format == EXPORT_FORMAT_APK && !p_path.ends_with(".apk")) { + EditorNode::get_singleton()->show_warning( + TTR("Invalid filename! Android APK requires the *.apk extension.")); + return ERR_UNCONFIGURED; + } + if (export_format > EXPORT_FORMAT_AAB || export_format < EXPORT_FORMAT_APK) { + EditorNode::add_io_error(TTR("Unsupported export format!\n")); + return ERR_UNCONFIGURED; //TODO: is this the right error? + } + + if (use_custom_build) { + print_verbose("Starting custom build.."); + //test that installed build version is alright + { + print_verbose("Checking build version.."); + FileAccessRef f = FileAccess::open("res://android/.build_version", FileAccess::READ); + if (!f) { + EditorNode::get_singleton()->show_warning(TTR("Trying to build from a custom built template, but no version info for it exists. Please reinstall from the 'Project' menu.")); + return ERR_UNCONFIGURED; + } + String version = f->get_line().strip_edges(); + print_verbose("- build version: " + version); + f->close(); + if (version != VERSION_FULL_CONFIG) { + EditorNode::get_singleton()->show_warning(vformat(TTR("Android build version mismatch:\n Template installed: %s\n Godot Version: %s\nPlease reinstall Android build template from 'Project' menu."), version, VERSION_FULL_CONFIG)); + return ERR_UNCONFIGURED; + } + } + String sdk_path = EDITOR_GET("export/android/android_sdk_path"); + ERR_FAIL_COND_V_MSG(sdk_path.is_empty(), ERR_UNCONFIGURED, "Android SDK path must be configured in Editor Settings at 'export/android/android_sdk_path'."); + print_verbose("Android sdk path: " + sdk_path); + + // TODO: should we use "package/name" or "application/config/name"? + String project_name = get_project_name(p_preset->get("package/name")); + err = _create_project_name_strings_files(p_preset, project_name); //project name localization. + if (err != OK) { + EditorNode::add_io_error(TTR("Unable to overwrite res://android/build/res/*.xml files with project name")); + } + // Copies the project icon files into the appropriate Gradle project directory. + _copy_icons_to_gradle_project(p_preset, processed_splash_config_xml, splash_image, splash_bg_color_image, main_image, foreground, background); + // Write an AndroidManifest.xml file into the Gradle project directory. + _write_tmp_manifest(p_preset, p_give_internet, p_debug); + + //stores all the project files inside the Gradle project directory. Also includes all ABIs + _clear_assets_directory(); + _remove_copied_libs(); + if (!apk_expansion) { + print_verbose("Exporting project files.."); + CustomExportData user_data; + user_data.debug = p_debug; + err = export_project_files(p_preset, rename_and_store_file_in_gradle_project, &user_data, copy_gradle_so); + if (err != OK) { + EditorNode::add_io_error(TTR("Could not export project files to gradle project\n")); + return err; + } + if (user_data.libs.size() > 0) { + FileAccessRef fa = FileAccess::open(GDNATIVE_LIBS_PATH, FileAccess::WRITE); + JSON json; + fa->store_string(json.stringify(user_data.libs, "\t")); + fa->close(); + } + } else { + print_verbose("Saving apk expansion file.."); + err = save_apk_expansion_file(p_preset, p_path); + if (err != OK) { + EditorNode::add_io_error(TTR("Could not write expansion package file!")); + return err; + } + } + print_verbose("Storing command line flags.."); + store_file_at_path("res://android/build/assets/_cl_", command_line_flags); + + print_verbose("Updating ANDROID_HOME environment to " + sdk_path); + OS::get_singleton()->set_environment("ANDROID_HOME", sdk_path); //set and overwrite if required + String build_command; + +#ifdef WINDOWS_ENABLED + build_command = "gradlew.bat"; +#else + build_command = "gradlew"; +#endif + + String build_path = ProjectSettings::get_singleton()->get_resource_path().plus_file("android/build"); + build_command = build_path.plus_file(build_command); + + String package_name = get_package_name(p_preset->get("package/unique_name")); + String version_code = itos(p_preset->get("version/code")); + String version_name = p_preset->get("version/name"); + String enabled_abi_string = String("|").join(enabled_abis); + String sign_flag = should_sign ? "true" : "false"; + String zipalign_flag = "true"; + + Vector<PluginConfigAndroid> enabled_plugins = get_enabled_plugins(p_preset); + String local_plugins_binaries = PluginConfigAndroid::get_plugins_binaries(PluginConfigAndroid::BINARY_TYPE_LOCAL, enabled_plugins); + String remote_plugins_binaries = PluginConfigAndroid::get_plugins_binaries(PluginConfigAndroid::BINARY_TYPE_REMOTE, enabled_plugins); + String custom_maven_repos = PluginConfigAndroid::get_plugins_custom_maven_repos(enabled_plugins); + bool clean_build_required = is_clean_build_required(enabled_plugins); + + List<String> cmdline; + if (clean_build_required) { + cmdline.push_back("clean"); + } + + String build_type = p_debug ? "Debug" : "Release"; + if (export_format == EXPORT_FORMAT_AAB) { + String bundle_build_command = vformat("bundle%s", build_type); + cmdline.push_back(bundle_build_command); + } else if (export_format == EXPORT_FORMAT_APK) { + String apk_build_command = vformat("assemble%s", build_type); + cmdline.push_back(apk_build_command); + } + + cmdline.push_back("-p"); // argument to specify the start directory. + cmdline.push_back(build_path); // start directory. + cmdline.push_back("-Pexport_package_name=" + package_name); // argument to specify the package name. + cmdline.push_back("-Pexport_version_code=" + version_code); // argument to specify the version code. + cmdline.push_back("-Pexport_version_name=" + version_name); // argument to specify the version name. + cmdline.push_back("-Pexport_enabled_abis=" + enabled_abi_string); // argument to specify enabled ABIs. + cmdline.push_back("-Pplugins_local_binaries=" + local_plugins_binaries); // argument to specify the list of plugins local dependencies. + cmdline.push_back("-Pplugins_remote_binaries=" + remote_plugins_binaries); // argument to specify the list of plugins remote dependencies. + cmdline.push_back("-Pplugins_maven_repos=" + custom_maven_repos); // argument to specify the list of custom maven repos for the plugins dependencies. + cmdline.push_back("-Pperform_zipalign=" + zipalign_flag); // argument to specify whether the build should be zipaligned. + cmdline.push_back("-Pperform_signing=" + sign_flag); // argument to specify whether the build should be signed. + cmdline.push_back("-Pgodot_editor_version=" + String(VERSION_FULL_CONFIG)); + + // NOTE: The release keystore is not included in the verbose logging + // to avoid accidentally leaking sensitive information when sharing verbose logs for troubleshooting. + // Any non-sensitive additions to the command line arguments must be done above this section. + // Sensitive additions must be done below the logging statement. + print_verbose("Build Android project using gradle command: " + String("\n") + build_command + " " + join_list(cmdline, String(" "))); + + if (should_sign) { + if (p_debug) { + String debug_keystore = p_preset->get("keystore/debug"); + String debug_password = p_preset->get("keystore/debug_password"); + String debug_user = p_preset->get("keystore/debug_user"); + + if (debug_keystore.is_empty()) { + debug_keystore = EditorSettings::get_singleton()->get("export/android/debug_keystore"); + debug_password = EditorSettings::get_singleton()->get("export/android/debug_keystore_pass"); + debug_user = EditorSettings::get_singleton()->get("export/android/debug_keystore_user"); + } + + cmdline.push_back("-Pdebug_keystore_file=" + debug_keystore); // argument to specify the debug keystore file. + cmdline.push_back("-Pdebug_keystore_alias=" + debug_user); // argument to specify the debug keystore alias. + cmdline.push_back("-Pdebug_keystore_password=" + debug_password); // argument to specify the debug keystore password. + } else { + // Pass the release keystore info as well + String release_keystore = p_preset->get("keystore/release"); + String release_username = p_preset->get("keystore/release_user"); + String release_password = p_preset->get("keystore/release_password"); + if (!FileAccess::exists(release_keystore)) { + EditorNode::add_io_error(TTR("Could not find keystore, unable to export.")); + return ERR_FILE_CANT_OPEN; + } + + cmdline.push_back("-Prelease_keystore_file=" + release_keystore); // argument to specify the release keystore file. + cmdline.push_back("-Prelease_keystore_alias=" + release_username); // argument to specify the release keystore alias. + cmdline.push_back("-Prelease_keystore_password=" + release_password); // argument to specify the release keystore password. + } + } + + int result = EditorNode::get_singleton()->execute_and_show_output(TTR("Building Android Project (gradle)"), build_command, cmdline); + if (result != 0) { + EditorNode::get_singleton()->show_warning(TTR("Building of Android project failed, check output for the error.\nAlternatively visit docs.godotengine.org for Android build documentation.")); + return ERR_CANT_CREATE; + } + + List<String> copy_args; + String copy_command; + if (export_format == EXPORT_FORMAT_AAB) { + copy_command = vformat("copyAndRename%sAab", build_type); + } else if (export_format == EXPORT_FORMAT_APK) { + copy_command = vformat("copyAndRename%sApk", build_type); + } + + copy_args.push_back(copy_command); + + copy_args.push_back("-p"); // argument to specify the start directory. + copy_args.push_back(build_path); // start directory. + + String export_filename = p_path.get_file(); + String export_path = p_path.get_base_dir(); + if (export_path.is_rel_path()) { + export_path = OS::get_singleton()->get_resource_dir().plus_file(export_path); + } + export_path = ProjectSettings::get_singleton()->globalize_path(export_path).simplify_path(); + + copy_args.push_back("-Pexport_path=file:" + export_path); + copy_args.push_back("-Pexport_filename=" + export_filename); + + print_verbose("Copying Android binary using gradle command: " + String("\n") + build_command + " " + join_list(copy_args, String(" "))); + int copy_result = EditorNode::get_singleton()->execute_and_show_output(TTR("Moving output"), build_command, copy_args); + if (copy_result != 0) { + EditorNode::get_singleton()->show_warning(TTR("Unable to copy and rename export file, check gradle project directory for outputs.")); + return ERR_CANT_CREATE; + } + + print_verbose("Successfully completed Android custom build."); + return OK; + } + // This is the start of the Legacy build system + print_verbose("Starting legacy build system.."); + if (p_debug) { + src_apk = p_preset->get("custom_template/debug"); + } else { + src_apk = p_preset->get("custom_template/release"); + } + src_apk = src_apk.strip_edges(); + if (src_apk == "") { + if (p_debug) { + src_apk = find_export_template("android_debug.apk"); + } else { + src_apk = find_export_template("android_release.apk"); + } + if (src_apk == "") { + EditorNode::add_io_error(vformat(TTR("Package not found: %s"), src_apk)); + return ERR_FILE_NOT_FOUND; + } + } + + if (!DirAccess::exists(p_path.get_base_dir())) { + return ERR_FILE_BAD_PATH; + } + + FileAccess *src_f = nullptr; + zlib_filefunc_def io = zipio_create_io_from_file(&src_f); + + if (ep.step(TTR("Creating APK..."), 0)) { + return ERR_SKIP; + } + + unzFile pkg = unzOpen2(src_apk.utf8().get_data(), &io); + if (!pkg) { + EditorNode::add_io_error(vformat(TTR("Could not find template APK to export:\n%s"), src_apk)); + return ERR_FILE_NOT_FOUND; + } + + int ret = unzGoToFirstFile(pkg); + + zlib_filefunc_def io2 = io; + FileAccess *dst_f = nullptr; + io2.opaque = &dst_f; + + String tmp_unaligned_path = EditorPaths::get_singleton()->get_cache_dir().plus_file("tmpexport-unaligned." + uitos(OS::get_singleton()->get_unix_time()) + ".apk"); + +#define CLEANUP_AND_RETURN(m_err) \ + { \ + DirAccess::remove_file_or_error(tmp_unaligned_path); \ + return m_err; \ + } + + zipFile unaligned_apk = zipOpen2(tmp_unaligned_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io2); + + String cmdline = p_preset->get("command_line/extra_args"); + + String version_name = p_preset->get("version/name"); + String package_name = p_preset->get("package/unique_name"); + + String apk_expansion_pkey = p_preset->get("apk_expansion/public_key"); + + Vector<String> invalid_abis(enabled_abis); + while (ret == UNZ_OK) { + //get filename + unz_file_info info; + char fname[16384]; + ret = unzGetCurrentFileInfo(pkg, &info, fname, 16384, nullptr, 0, nullptr, 0); + + bool skip = false; + + String file = fname; + + Vector<uint8_t> data; + data.resize(info.uncompressed_size); + + //read + unzOpenCurrentFile(pkg); + unzReadCurrentFile(pkg, data.ptrw(), data.size()); + unzCloseCurrentFile(pkg); + + //write + if (file == "AndroidManifest.xml") { + _fix_manifest(p_preset, data, p_give_internet); + } + if (file == "resources.arsc") { + _fix_resources(p_preset, data); + } + + // Process the splash image + if ((file == SPLASH_IMAGE_EXPORT_PATH || file == LEGACY_BUILD_SPLASH_IMAGE_EXPORT_PATH) && splash_image.is_valid() && !splash_image->is_empty()) { + _load_image_data(splash_image, data); + } + + // Process the splash bg color image + if ((file == SPLASH_BG_COLOR_PATH || file == LEGACY_BUILD_SPLASH_BG_COLOR_PATH) && splash_bg_color_image.is_valid() && !splash_bg_color_image->is_empty()) { + _load_image_data(splash_bg_color_image, data); + } + + for (int i = 0; i < icon_densities_count; ++i) { + if (main_image.is_valid() && !main_image->is_empty()) { + if (file == launcher_icons[i].export_path) { + _process_launcher_icons(file, main_image, launcher_icons[i].dimensions, data); + } + } + if (foreground.is_valid() && !foreground->is_empty()) { + if (file == launcher_adaptive_icon_foregrounds[i].export_path) { + _process_launcher_icons(file, foreground, launcher_adaptive_icon_foregrounds[i].dimensions, data); + } + } + if (background.is_valid() && !background->is_empty()) { + if (file == launcher_adaptive_icon_backgrounds[i].export_path) { + _process_launcher_icons(file, background, launcher_adaptive_icon_backgrounds[i].dimensions, data); + } + } + } + + if (file.ends_with(".so")) { + bool enabled = false; + for (int i = 0; i < enabled_abis.size(); ++i) { + if (file.begins_with("lib/" + enabled_abis[i] + "/")) { + invalid_abis.erase(enabled_abis[i]); + enabled = true; + break; + } + } + if (!enabled) { + skip = true; + } + } + + if (file.begins_with("META-INF") && should_sign) { + skip = true; + } + + if (!skip) { + print_line("ADDING: " + file); + + // Respect decision on compression made by AAPT for the export template + const bool uncompressed = info.compression_method == 0; + + zip_fileinfo zipfi = get_zip_fileinfo(); + + zipOpenNewFileInZip(unaligned_apk, + file.utf8().get_data(), + &zipfi, + nullptr, + 0, + nullptr, + 0, + nullptr, + uncompressed ? 0 : Z_DEFLATED, + Z_DEFAULT_COMPRESSION); + + zipWriteInFileInZip(unaligned_apk, data.ptr(), data.size()); + zipCloseFileInZip(unaligned_apk); + } + + ret = unzGoToNextFile(pkg); + } + + if (!invalid_abis.is_empty()) { + String unsupported_arch = String(", ").join(invalid_abis); + EditorNode::add_io_error(vformat(TTR("Missing libraries in the export template for the selected architectures: %s.\nPlease build a template with all required libraries, or uncheck the missing architectures in the export preset."), unsupported_arch)); + CLEANUP_AND_RETURN(ERR_FILE_NOT_FOUND); + } + + if (ep.step(TTR("Adding files..."), 1)) { + CLEANUP_AND_RETURN(ERR_SKIP); + } + err = OK; + + if (p_flags & DEBUG_FLAG_DUMB_CLIENT) { + APKExportData ed; + ed.ep = &ep; + ed.apk = unaligned_apk; + err = export_project_files(p_preset, ignore_apk_file, &ed, save_apk_so); + } else { + if (apk_expansion) { + err = save_apk_expansion_file(p_preset, p_path); + if (err != OK) { + EditorNode::add_io_error(TTR("Could not write expansion package file!")); + return err; + } + } else { + APKExportData ed; + ed.ep = &ep; + ed.apk = unaligned_apk; + err = export_project_files(p_preset, save_apk_file, &ed, save_apk_so); + } + } + + if (err != OK) { + unzClose(pkg); + EditorNode::add_io_error(TTR("Could not export project files")); + CLEANUP_AND_RETURN(ERR_SKIP); + } + + zip_fileinfo zipfi = get_zip_fileinfo(); + zipOpenNewFileInZip(unaligned_apk, + "assets/_cl_", + &zipfi, + nullptr, + 0, + nullptr, + 0, + nullptr, + 0, // No compress (little size gain and potentially slower startup) + Z_DEFAULT_COMPRESSION); + zipWriteInFileInZip(unaligned_apk, command_line_flags.ptr(), command_line_flags.size()); + zipCloseFileInZip(unaligned_apk); + zipClose(unaligned_apk, nullptr); + unzClose(pkg); + + if (err != OK) { + CLEANUP_AND_RETURN(err); + } + + // Let's zip-align (must be done before signing) + + static const int ZIP_ALIGNMENT = 4; + + // If we're not signing the apk, then the next step should be the last. + const int next_step = should_sign ? 103 : 105; + if (ep.step(TTR("Aligning APK..."), next_step)) { + CLEANUP_AND_RETURN(ERR_SKIP); + } + + unzFile tmp_unaligned = unzOpen2(tmp_unaligned_path.utf8().get_data(), &io); + if (!tmp_unaligned) { + EditorNode::add_io_error(TTR("Could not unzip temporary unaligned APK.")); + CLEANUP_AND_RETURN(ERR_FILE_NOT_FOUND); + } + + ret = unzGoToFirstFile(tmp_unaligned); + + io2 = io; + dst_f = nullptr; + io2.opaque = &dst_f; + zipFile final_apk = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io2); + + // Take files from the unaligned APK and write them out to the aligned one + // in raw mode, i.e. not uncompressing and recompressing, aligning them as needed, + // following what is done in https://github.com/android/platform_build/blob/master/tools/zipalign/ZipAlign.cpp + int bias = 0; + while (ret == UNZ_OK) { + unz_file_info info; + memset(&info, 0, sizeof(info)); + + char fname[16384]; + char extra[16384]; + ret = unzGetCurrentFileInfo(tmp_unaligned, &info, fname, 16384, extra, 16384 - ZIP_ALIGNMENT, nullptr, 0); + + String file = fname; + + Vector<uint8_t> data; + data.resize(info.compressed_size); + + // read + int method, level; + unzOpenCurrentFile2(tmp_unaligned, &method, &level, 1); // raw read + long file_offset = unzGetCurrentFileZStreamPos64(tmp_unaligned); + unzReadCurrentFile(tmp_unaligned, data.ptrw(), data.size()); + unzCloseCurrentFile(tmp_unaligned); + + // align + int padding = 0; + if (!info.compression_method) { + // Uncompressed file => Align + long new_offset = file_offset + bias; + padding = (ZIP_ALIGNMENT - (new_offset % ZIP_ALIGNMENT)) % ZIP_ALIGNMENT; + } + + memset(extra + info.size_file_extra, 0, padding); + + zip_fileinfo fileinfo = get_zip_fileinfo(); + zipOpenNewFileInZip2(final_apk, + file.utf8().get_data(), + &fileinfo, + extra, + info.size_file_extra + padding, + nullptr, + 0, + nullptr, + method, + level, + 1); // raw write + zipWriteInFileInZip(final_apk, data.ptr(), data.size()); + zipCloseFileInZipRaw(final_apk, info.uncompressed_size, info.crc); + + bias += padding; + + ret = unzGoToNextFile(tmp_unaligned); + } + + zipClose(final_apk, nullptr); + unzClose(tmp_unaligned); + + if (should_sign) { + // Signing must be done last as any additional modifications to the + // file will invalidate the signature. + err = sign_apk(p_preset, p_debug, p_path, ep); + if (err != OK) { + CLEANUP_AND_RETURN(err); + } + } + + CLEANUP_AND_RETURN(OK); +} + +void EditorExportPlatformAndroid::get_platform_features(List<String> *r_features) { + r_features->push_back("mobile"); + r_features->push_back("Android"); +} + +void EditorExportPlatformAndroid::resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) { +} + +EditorExportPlatformAndroid::EditorExportPlatformAndroid() { + Ref<Image> img = memnew(Image(_android_logo)); + logo.instantiate(); + logo->create_from_image(img); + + img = Ref<Image>(memnew(Image(_android_run_icon))); + run_icon.instantiate(); + run_icon->create_from_image(img); + + devices_changed.set(); + plugins_changed.set(); + check_for_changes_thread.start(_check_for_changes_poll_thread, this); +} + +EditorExportPlatformAndroid::~EditorExportPlatformAndroid() { + quit_request.set(); + check_for_changes_thread.wait_to_finish(); +} diff --git a/platform/android/export/export_plugin.h b/platform/android/export/export_plugin.h new file mode 100644 index 0000000000..909428c2fe --- /dev/null +++ b/platform/android/export/export_plugin.h @@ -0,0 +1,255 @@ +/*************************************************************************/ +/* export_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "core/config/project_settings.h" +#include "core/io/dir_access.h" +#include "core/io/file_access.h" +#include "core/io/image_loader.h" +#include "core/io/json.h" +#include "core/io/marshalls.h" +#include "core/io/zip_io.h" +#include "core/os/os.h" +#include "core/templates/safe_refcount.h" +#include "core/version.h" +#include "drivers/png/png_driver_common.h" +#include "editor/editor_export.h" +#include "editor/editor_log.h" +#include "editor/editor_node.h" +#include "editor/editor_settings.h" +#include "main/splash.gen.h" +#include "platform/android/logo.gen.h" +#include "platform/android/run_icon.gen.h" + +#include "godot_plugin_config.h" +#include "gradle_export_util.h" + +#include <string.h> + +const String SPLASH_CONFIG_XML_CONTENT = R"SPLASH(<?xml version="1.0" encoding="utf-8"?> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:drawable="@drawable/splash_bg_color" /> + <item> + <bitmap + android:gravity="center" + android:filter="%s" + android:src="@drawable/splash" /> + </item> +</layer-list> +)SPLASH"; + +struct LauncherIcon { + const char *export_path; + int dimensions = 0; +}; + +class EditorExportPlatformAndroid : public EditorExportPlatform { + GDCLASS(EditorExportPlatformAndroid, EditorExportPlatform); + + Ref<ImageTexture> logo; + Ref<ImageTexture> run_icon; + + struct Device { + String id; + String name; + String description; + int api_level = 0; + }; + + struct APKExportData { + zipFile apk; + EditorProgress *ep = nullptr; + }; + + struct CustomExportData { + bool debug; + Vector<String> libs; + }; + + Vector<PluginConfigAndroid> plugins; + String last_plugin_names; + uint64_t last_custom_build_time = 0; + SafeFlag plugins_changed; + Mutex plugins_lock; + Vector<Device> devices; + SafeFlag devices_changed; + Mutex device_lock; + Thread check_for_changes_thread; + SafeFlag quit_request; + + static void _check_for_changes_poll_thread(void *ud); + + String get_project_name(const String &p_name) const; + + String get_package_name(const String &p_package) const; + + bool is_package_name_valid(const String &p_package, String *r_error = nullptr) const; + + static bool _should_compress_asset(const String &p_path, const Vector<uint8_t> &p_data); + + static zip_fileinfo get_zip_fileinfo(); + + static Vector<String> get_abis(); + + /// List the gdap files in the directory specified by the p_path parameter. + static Vector<String> list_gdap_files(const String &p_path); + + static Vector<PluginConfigAndroid> get_plugins(); + + static Vector<PluginConfigAndroid> get_enabled_plugins(const Ref<EditorExportPreset> &p_presets); + + static Error store_in_apk(APKExportData *ed, const String &p_path, const Vector<uint8_t> &p_data, int compression_method = Z_DEFLATED); + + static Error save_apk_so(void *p_userdata, const SharedObject &p_so); + + static Error save_apk_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key); + + static Error ignore_apk_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key); + + static Error copy_gradle_so(void *p_userdata, const SharedObject &p_so); + + void _get_permissions(const Ref<EditorExportPreset> &p_preset, bool p_give_internet, Vector<String> &r_permissions); + + void _write_tmp_manifest(const Ref<EditorExportPreset> &p_preset, bool p_give_internet, bool p_debug); + + void _fix_manifest(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_manifest, bool p_give_internet); + + static String _parse_string(const uint8_t *p_bytes, bool p_utf8); + + void _fix_resources(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &r_manifest); + + void _load_image_data(const Ref<Image> &p_splash_image, Vector<uint8_t> &p_data); + + void _process_launcher_icons(const String &p_file_name, const Ref<Image> &p_source_image, int dimension, Vector<uint8_t> &p_data); + + String load_splash_refs(Ref<Image> &splash_image, Ref<Image> &splash_bg_color_image); + + void load_icon_refs(const Ref<EditorExportPreset> &p_preset, Ref<Image> &icon, Ref<Image> &foreground, Ref<Image> &background); + + void store_image(const LauncherIcon launcher_icon, const Vector<uint8_t> &data); + + void store_image(const String &export_path, const Vector<uint8_t> &data); + + void _copy_icons_to_gradle_project(const Ref<EditorExportPreset> &p_preset, + const String &processed_splash_config_xml, + const Ref<Image> &splash_image, + const Ref<Image> &splash_bg_color_image, + const Ref<Image> &main_image, + const Ref<Image> &foreground, + const Ref<Image> &background); + + static Vector<String> get_enabled_abis(const Ref<EditorExportPreset> &p_preset); + +public: + typedef Error (*EditorExportSaveFunction)(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key); + +public: + virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) override; + + virtual void get_export_options(List<ExportOption> *r_options) override; + + virtual String get_name() const override; + + virtual String get_os_name() const override; + + virtual Ref<Texture2D> get_logo() const override; + + virtual bool should_update_export_options() override; + + virtual bool poll_export() override; + + virtual int get_options_count() const override; + + virtual String get_options_tooltip() const override; + + virtual String get_option_label(int p_index) const override; + + virtual String get_option_tooltip(int p_index) const override; + + virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, int p_debug_flags) override; + + virtual Ref<Texture2D> get_run_icon() const override; + + static String get_adb_path(); + + static String get_apksigner_path(); + + virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override; + + virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override; + + inline bool is_clean_build_required(Vector<PluginConfigAndroid> enabled_plugins) { + String plugin_names = PluginConfigAndroid::get_plugins_names(enabled_plugins); + bool first_build = last_custom_build_time == 0; + bool have_plugins_changed = false; + + if (!first_build) { + have_plugins_changed = plugin_names != last_plugin_names; + if (!have_plugins_changed) { + for (int i = 0; i < enabled_plugins.size(); i++) { + if (enabled_plugins.get(i).last_updated > last_custom_build_time) { + have_plugins_changed = true; + break; + } + } + } + } + + last_custom_build_time = OS::get_singleton()->get_unix_time(); + last_plugin_names = plugin_names; + + return have_plugins_changed || first_build; + } + + String get_apk_expansion_fullpath(const Ref<EditorExportPreset> &p_preset, const String &p_path); + + Error save_apk_expansion_file(const Ref<EditorExportPreset> &p_preset, const String &p_path); + + void get_command_line_flags(const Ref<EditorExportPreset> &p_preset, const String &p_path, int p_flags, Vector<uint8_t> &r_command_line_flags); + + Error sign_apk(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &export_path, EditorProgress &ep); + + void _clear_assets_directory(); + + void _remove_copied_libs(); + + String join_list(List<String> parts, const String &separator) const; + + virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override; + + Error export_project_helper(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int export_format, bool should_sign, int p_flags); + + virtual void get_platform_features(List<String> *r_features) override; + + virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) override; + + EditorExportPlatformAndroid(); + + ~EditorExportPlatformAndroid(); +}; diff --git a/platform/android/plugin/godot_plugin_config.h b/platform/android/export/godot_plugin_config.cpp index 6b708548ae..ba7b8ce6c7 100644 --- a/platform/android/plugin/godot_plugin_config.h +++ b/platform/android/export/godot_plugin_config.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* godot_plugin_config.h */ +/* godot_plugin_config.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,78 +28,23 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef GODOT_PLUGIN_CONFIG_H -#define GODOT_PLUGIN_CONFIG_H - -#include "core/error/error_list.h" -#include "core/io/config_file.h" -#include "core/string/ustring.h" - -/* - The `config` section and fields are required and defined as follow: -- **name**: name of the plugin. -- **binary_type**: can be either `local` or `remote`. The type affects the **binary** field. -- **binary**: - - if **binary_type** is `local`, then this should be the filename of the plugin `aar` file in the `res://android/plugins` directory (e.g: `MyPlugin.aar`). - - if **binary_type** is `remote`, then this should be a declaration for a remote gradle binary (e.g: "org.godot.example:my-plugin:0.0.0"). - -The `dependencies` section and fields are optional and defined as follow: -- **local**: contains a list of local `.aar` binary files the plugin depends on. The local binary dependencies must also be located in the `res://android/plugins` directory. -- **remote**: contains a list of remote binary gradle dependencies for the plugin. -- **custom_maven_repos**: contains a list of urls specifying custom maven repos required for the plugin's dependencies. - - See https://github.com/godotengine/godot/issues/38157#issuecomment-618773871 - */ -struct PluginConfigAndroid { - inline static const char *PLUGIN_CONFIG_EXT = ".gdap"; - - inline static const char *CONFIG_SECTION = "config"; - inline static const char *CONFIG_NAME_KEY = "name"; - inline static const char *CONFIG_BINARY_TYPE_KEY = "binary_type"; - inline static const char *CONFIG_BINARY_KEY = "binary"; - - inline static const char *DEPENDENCIES_SECTION = "dependencies"; - inline static const char *DEPENDENCIES_LOCAL_KEY = "local"; - inline static const char *DEPENDENCIES_REMOTE_KEY = "remote"; - inline static const char *DEPENDENCIES_CUSTOM_MAVEN_REPOS_KEY = "custom_maven_repos"; - - inline static const char *BINARY_TYPE_LOCAL = "local"; - inline static const char *BINARY_TYPE_REMOTE = "remote"; - - inline static const char *PLUGIN_VALUE_SEPARATOR = "|"; - - // Set to true when the config file is properly loaded. - bool valid_config = false; - // Unix timestamp of last change to this plugin. - uint64_t last_updated = 0; - - // Required config section - String name; - String binary_type; - String binary; - - // Optional dependencies section - Vector<String> local_dependencies; - Vector<String> remote_dependencies; - Vector<String> custom_maven_repos; -}; - +#include "godot_plugin_config.h" /* * Set of prebuilt plugins. * Currently unused, this is just for future reference: */ // static const PluginConfigAndroid MY_PREBUILT_PLUGIN = { -// /*.valid_config =*/true, -// /*.last_updated =*/0, -// /*.name =*/"GodotPayment", -// /*.binary_type =*/"local", -// /*.binary =*/"res://android/build/libs/plugins/GodotPayment.release.aar", -// /*.local_dependencies =*/{}, -// /*.remote_dependencies =*/String("com.android.billingclient:billing:2.2.1").split("|"), -// /*.custom_maven_repos =*/{} +// /*.valid_config =*/true, +// /*.last_updated =*/0, +// /*.name =*/"GodotPayment", +// /*.binary_type =*/"local", +// /*.binary =*/"res://android/build/libs/plugins/GodotPayment.release.aar", +// /*.local_dependencies =*/{}, +// /*.remote_dependencies =*/String("com.android.billingclient:billing:2.2.1").split("|"), +// /*.custom_maven_repos =*/{} // }; -static inline String resolve_local_dependency_path(String plugin_config_dir, String dependency_path) { +String PluginConfigAndroid::resolve_local_dependency_path(String plugin_config_dir, String dependency_path) { String absolute_path; if (!dependency_path.is_empty()) { if (dependency_path.is_absolute_path()) { @@ -112,7 +57,7 @@ static inline String resolve_local_dependency_path(String plugin_config_dir, Str return absolute_path; } -static inline PluginConfigAndroid resolve_prebuilt_plugin(PluginConfigAndroid prebuilt_plugin, String plugin_config_dir) { +PluginConfigAndroid PluginConfigAndroid::resolve_prebuilt_plugin(PluginConfigAndroid prebuilt_plugin, String plugin_config_dir) { PluginConfigAndroid resolved = prebuilt_plugin; resolved.binary = resolved.binary_type == PluginConfigAndroid::BINARY_TYPE_LOCAL ? resolve_local_dependency_path(plugin_config_dir, prebuilt_plugin.binary) : prebuilt_plugin.binary; if (!prebuilt_plugin.local_dependencies.is_empty()) { @@ -124,13 +69,13 @@ static inline PluginConfigAndroid resolve_prebuilt_plugin(PluginConfigAndroid pr return resolved; } -static inline Vector<PluginConfigAndroid> get_prebuilt_plugins(String plugins_base_dir) { +Vector<PluginConfigAndroid> PluginConfigAndroid::get_prebuilt_plugins(String plugins_base_dir) { Vector<PluginConfigAndroid> prebuilt_plugins; // prebuilt_plugins.push_back(resolve_prebuilt_plugin(MY_PREBUILT_PLUGIN, plugins_base_dir)); return prebuilt_plugins; } -static inline bool is_plugin_config_valid(PluginConfigAndroid plugin_config) { +bool PluginConfigAndroid::is_plugin_config_valid(PluginConfigAndroid plugin_config) { bool valid_name = !plugin_config.name.is_empty(); bool valid_binary_type = plugin_config.binary_type == PluginConfigAndroid::BINARY_TYPE_LOCAL || plugin_config.binary_type == PluginConfigAndroid::BINARY_TYPE_REMOTE; @@ -155,7 +100,7 @@ static inline bool is_plugin_config_valid(PluginConfigAndroid plugin_config) { return valid_name && valid_binary && valid_binary_type && valid_local_dependencies; } -static inline uint64_t get_plugin_modification_time(const PluginConfigAndroid &plugin_config, const String &config_path) { +uint64_t PluginConfigAndroid::get_plugin_modification_time(const PluginConfigAndroid &plugin_config, const String &config_path) { uint64_t last_updated = FileAccess::get_modified_time(config_path); last_updated = MAX(last_updated, FileAccess::get_modified_time(plugin_config.binary)); @@ -167,7 +112,7 @@ static inline uint64_t get_plugin_modification_time(const PluginConfigAndroid &p return last_updated; } -static inline PluginConfigAndroid load_plugin_config(Ref<ConfigFile> config_file, const String &path) { +PluginConfigAndroid PluginConfigAndroid::load_plugin_config(Ref<ConfigFile> config_file, const String &path) { PluginConfigAndroid plugin_config = {}; if (config_file.is_valid()) { @@ -201,7 +146,7 @@ static inline PluginConfigAndroid load_plugin_config(Ref<ConfigFile> config_file return plugin_config; } -static inline String get_plugins_binaries(String binary_type, Vector<PluginConfigAndroid> plugins_configs) { +String PluginConfigAndroid::get_plugins_binaries(String binary_type, Vector<PluginConfigAndroid> plugins_configs) { String plugins_binaries; if (!plugins_configs.is_empty()) { Vector<String> binaries; @@ -230,7 +175,7 @@ static inline String get_plugins_binaries(String binary_type, Vector<PluginConfi return plugins_binaries; } -static inline String get_plugins_custom_maven_repos(Vector<PluginConfigAndroid> plugins_configs) { +String PluginConfigAndroid::get_plugins_custom_maven_repos(Vector<PluginConfigAndroid> plugins_configs) { String custom_maven_repos; if (!plugins_configs.is_empty()) { Vector<String> repos_urls; @@ -248,7 +193,7 @@ static inline String get_plugins_custom_maven_repos(Vector<PluginConfigAndroid> return custom_maven_repos; } -static inline String get_plugins_names(Vector<PluginConfigAndroid> plugins_configs) { +String PluginConfigAndroid::get_plugins_names(Vector<PluginConfigAndroid> plugins_configs) { String plugins_names; if (!plugins_configs.is_empty()) { Vector<String> names; @@ -265,5 +210,3 @@ static inline String get_plugins_names(Vector<PluginConfigAndroid> plugins_confi return plugins_names; } - -#endif // GODOT_PLUGIN_CONFIG_H diff --git a/platform/android/export/godot_plugin_config.h b/platform/android/export/godot_plugin_config.h new file mode 100644 index 0000000000..c016634371 --- /dev/null +++ b/platform/android/export/godot_plugin_config.h @@ -0,0 +1,106 @@ +/*************************************************************************/ +/* godot_plugin_config.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef ANDROID_GODOT_PLUGIN_CONFIG_H +#define ANDROID_GODOT_PLUGIN_CONFIG_H + +#include "core/config/project_settings.h" +#include "core/error/error_list.h" +#include "core/io/config_file.h" +#include "core/string/ustring.h" + +/* + The `config` section and fields are required and defined as follow: +- **name**: name of the plugin. +- **binary_type**: can be either `local` or `remote`. The type affects the **binary** field. +- **binary**: + - if **binary_type** is `local`, then this should be the filename of the plugin `aar` file in the `res://android/plugins` directory (e.g: `MyPlugin.aar`). + - if **binary_type** is `remote`, then this should be a declaration for a remote gradle binary (e.g: "org.godot.example:my-plugin:0.0.0"). + +The `dependencies` section and fields are optional and defined as follow: +- **local**: contains a list of local `.aar` binary files the plugin depends on. The local binary dependencies must also be located in the `res://android/plugins` directory. +- **remote**: contains a list of remote binary gradle dependencies for the plugin. +- **custom_maven_repos**: contains a list of urls specifying custom maven repos required for the plugin's dependencies. + + See https://github.com/godotengine/godot/issues/38157#issuecomment-618773871 + */ +struct PluginConfigAndroid { + inline static const char *PLUGIN_CONFIG_EXT = ".gdap"; + + inline static const char *CONFIG_SECTION = "config"; + inline static const char *CONFIG_NAME_KEY = "name"; + inline static const char *CONFIG_BINARY_TYPE_KEY = "binary_type"; + inline static const char *CONFIG_BINARY_KEY = "binary"; + + inline static const char *DEPENDENCIES_SECTION = "dependencies"; + inline static const char *DEPENDENCIES_LOCAL_KEY = "local"; + inline static const char *DEPENDENCIES_REMOTE_KEY = "remote"; + inline static const char *DEPENDENCIES_CUSTOM_MAVEN_REPOS_KEY = "custom_maven_repos"; + + inline static const char *BINARY_TYPE_LOCAL = "local"; + inline static const char *BINARY_TYPE_REMOTE = "remote"; + + inline static const char *PLUGIN_VALUE_SEPARATOR = "|"; + + // Set to true when the config file is properly loaded. + bool valid_config = false; + // Unix timestamp of last change to this plugin. + uint64_t last_updated = 0; + + // Required config section + String name; + String binary_type; + String binary; + + // Optional dependencies section + Vector<String> local_dependencies; + Vector<String> remote_dependencies; + Vector<String> custom_maven_repos; + + static String resolve_local_dependency_path(String plugin_config_dir, String dependency_path); + + static PluginConfigAndroid resolve_prebuilt_plugin(PluginConfigAndroid prebuilt_plugin, String plugin_config_dir); + + static Vector<PluginConfigAndroid> get_prebuilt_plugins(String plugins_base_dir); + + static bool is_plugin_config_valid(PluginConfigAndroid plugin_config); + + static uint64_t get_plugin_modification_time(const PluginConfigAndroid &plugin_config, const String &config_path); + + static PluginConfigAndroid load_plugin_config(Ref<ConfigFile> config_file, const String &path); + + static String get_plugins_binaries(String binary_type, Vector<PluginConfigAndroid> plugins_configs); + + static String get_plugins_custom_maven_repos(Vector<PluginConfigAndroid> plugins_configs); + + static String get_plugins_names(Vector<PluginConfigAndroid> plugins_configs); +}; + +#endif // GODOT_PLUGIN_CONFIG_H diff --git a/platform/android/export/gradle_export_util.cpp b/platform/android/export/gradle_export_util.cpp new file mode 100644 index 0000000000..354287d872 --- /dev/null +++ b/platform/android/export/gradle_export_util.cpp @@ -0,0 +1,252 @@ +/*************************************************************************/ +/* gradle_export_util.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "gradle_export_util.h" + +int _get_android_orientation_value(DisplayServer::ScreenOrientation screen_orientation) { + switch (screen_orientation) { + case DisplayServer::SCREEN_PORTRAIT: + return 1; + case DisplayServer::SCREEN_REVERSE_LANDSCAPE: + return 8; + case DisplayServer::SCREEN_REVERSE_PORTRAIT: + return 9; + case DisplayServer::SCREEN_SENSOR_LANDSCAPE: + return 11; + case DisplayServer::SCREEN_SENSOR_PORTRAIT: + return 12; + case DisplayServer::SCREEN_SENSOR: + return 13; + case DisplayServer::SCREEN_LANDSCAPE: + default: + return 0; + } +} + +String _get_android_orientation_label(DisplayServer::ScreenOrientation screen_orientation) { + switch (screen_orientation) { + case DisplayServer::SCREEN_PORTRAIT: + return "portrait"; + case DisplayServer::SCREEN_REVERSE_LANDSCAPE: + return "reverseLandscape"; + case DisplayServer::SCREEN_REVERSE_PORTRAIT: + return "reversePortrait"; + case DisplayServer::SCREEN_SENSOR_LANDSCAPE: + return "userLandscape"; + case DisplayServer::SCREEN_SENSOR_PORTRAIT: + return "userPortrait"; + case DisplayServer::SCREEN_SENSOR: + return "fullUser"; + case DisplayServer::SCREEN_LANDSCAPE: + default: + return "landscape"; + } +} + +// Utility method used to create a directory. +Error create_directory(const String &p_dir) { + if (!DirAccess::exists(p_dir)) { + DirAccess *filesystem_da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + ERR_FAIL_COND_V_MSG(!filesystem_da, ERR_CANT_CREATE, "Cannot create directory '" + p_dir + "'."); + Error err = filesystem_da->make_dir_recursive(p_dir); + ERR_FAIL_COND_V_MSG(err, ERR_CANT_CREATE, "Cannot create directory '" + p_dir + "'."); + memdelete(filesystem_da); + } + return OK; +} + +// Writes p_data into a file at p_path, creating directories if necessary. +// Note: this will overwrite the file at p_path if it already exists. +Error store_file_at_path(const String &p_path, const Vector<uint8_t> &p_data) { + String dir = p_path.get_base_dir(); + Error err = create_directory(dir); + if (err != OK) { + return err; + } + FileAccess *fa = FileAccess::open(p_path, FileAccess::WRITE); + ERR_FAIL_COND_V_MSG(!fa, ERR_CANT_CREATE, "Cannot create file '" + p_path + "'."); + fa->store_buffer(p_data.ptr(), p_data.size()); + memdelete(fa); + return OK; +} + +// Writes string p_data into a file at p_path, creating directories if necessary. +// Note: this will overwrite the file at p_path if it already exists. +Error store_string_at_path(const String &p_path, const String &p_data) { + String dir = p_path.get_base_dir(); + Error err = create_directory(dir); + if (err != OK) { + if (OS::get_singleton()->is_stdout_verbose()) { + print_error("Unable to write data into " + p_path); + } + return err; + } + FileAccess *fa = FileAccess::open(p_path, FileAccess::WRITE); + ERR_FAIL_COND_V_MSG(!fa, ERR_CANT_CREATE, "Cannot create file '" + p_path + "'."); + fa->store_string(p_data); + memdelete(fa); + return OK; +} + +// Implementation of EditorExportSaveFunction. +// This method will only be called as an input to export_project_files. +// It is used by the export_project_files method to save all the asset files into the gradle project. +// It's functionality mirrors that of the method save_apk_file. +// This method will be called ONLY when custom build is enabled. +Error rename_and_store_file_in_gradle_project(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key) { + String dst_path = p_path.replace_first("res://", "res://android/build/assets/"); + print_verbose("Saving project files from " + p_path + " into " + dst_path); + Error err = store_file_at_path(dst_path, p_data); + return err; +} + +// Creates strings.xml files inside the gradle project for different locales. +Error _create_project_name_strings_files(const Ref<EditorExportPreset> &p_preset, const String &project_name) { + print_verbose("Creating strings resources for supported locales for project " + project_name); + // Stores the string into the default values directory. + String processed_default_xml_string = vformat(godot_project_name_xml_string, project_name.xml_escape(true)); + store_string_at_path("res://android/build/res/values/godot_project_name_string.xml", processed_default_xml_string); + + // Searches the Gradle project res/ directory to find all supported locales + DirAccessRef da = DirAccess::open("res://android/build/res"); + if (!da) { + if (OS::get_singleton()->is_stdout_verbose()) { + print_error("Unable to open Android resources directory."); + } + return ERR_CANT_OPEN; + } + da->list_dir_begin(); + while (true) { + String file = da->get_next(); + if (file == "") { + break; + } + if (!file.begins_with("values-")) { + // NOTE: This assumes all directories that start with "values-" are for localization. + continue; + } + String locale = file.replace("values-", "").replace("-r", "_"); + String property_name = "application/config/name_" + locale; + String locale_directory = "res://android/build/res/" + file + "/godot_project_name_string.xml"; + if (ProjectSettings::get_singleton()->has_setting(property_name)) { + String locale_project_name = ProjectSettings::get_singleton()->get(property_name); + String processed_xml_string = vformat(godot_project_name_xml_string, locale_project_name.xml_escape(true)); + print_verbose("Storing project name for locale " + locale + " under " + locale_directory); + store_string_at_path(locale_directory, processed_xml_string); + } else { + // TODO: Once the legacy build system is deprecated we don't need to have xml files for this else branch + store_string_at_path(locale_directory, processed_default_xml_string); + } + } + da->list_dir_end(); + return OK; +} + +String bool_to_string(bool v) { + return v ? "true" : "false"; +} + +String _get_gles_tag() { + bool min_gles3 = ProjectSettings::get_singleton()->get("rendering/driver/driver_name") == "GLES3" && + !ProjectSettings::get_singleton()->get("rendering/driver/fallback_to_gles2"); + return min_gles3 ? " <uses-feature android:glEsVersion=\"0x00030000\" android:required=\"true\" />\n" : ""; +} + +String _get_screen_sizes_tag(const Ref<EditorExportPreset> &p_preset) { + String manifest_screen_sizes = " <supports-screens \n tools:node=\"replace\""; + String sizes[] = { "small", "normal", "large", "xlarge" }; + size_t num_sizes = sizeof(sizes) / sizeof(sizes[0]); + for (size_t i = 0; i < num_sizes; i++) { + String feature_name = vformat("screen/support_%s", sizes[i]); + String feature_support = bool_to_string(p_preset->get(feature_name)); + String xml_entry = vformat("\n android:%sScreens=\"%s\"", sizes[i], feature_support); + manifest_screen_sizes += xml_entry; + } + manifest_screen_sizes += " />\n"; + return manifest_screen_sizes; +} + +String _get_xr_features_tag(const Ref<EditorExportPreset> &p_preset) { + String manifest_xr_features; + bool uses_xr = (int)(p_preset->get("xr_features/xr_mode")) == 1; + if (uses_xr) { + int hand_tracking_index = p_preset->get("xr_features/hand_tracking"); // 0: none, 1: optional, 2: required + if (hand_tracking_index == 1) { + manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"oculus.software.handtracking\" android:required=\"false\" />\n"; + } else if (hand_tracking_index == 2) { + manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"oculus.software.handtracking\" android:required=\"true\" />\n"; + } + } + return manifest_xr_features; +} + +String _get_instrumentation_tag(const Ref<EditorExportPreset> &p_preset) { + String package_name = p_preset->get("package/unique_name"); + String manifest_instrumentation_text = vformat( + " <instrumentation\n" + " tools:node=\"replace\"\n" + " android:name=\".GodotInstrumentation\"\n" + " android:icon=\"@mipmap/icon\"\n" + " android:label=\"@string/godot_project_name_string\"\n" + " android:targetPackage=\"%s\" />\n", + package_name); + return manifest_instrumentation_text; +} + +String _get_activity_tag(const Ref<EditorExportPreset> &p_preset) { + bool uses_xr = (int)(p_preset->get("xr_features/xr_mode")) == 1; + String orientation = _get_android_orientation_label(DisplayServer::ScreenOrientation(int(GLOBAL_GET("display/window/handheld/orientation")))); + String manifest_activity_text = vformat( + " <activity android:name=\"com.godot.game.GodotApp\" " + "tools:replace=\"android:screenOrientation\" " + "android:screenOrientation=\"%s\">\n", + orientation); + if (!uses_xr) { + manifest_activity_text += " <meta-data tools:node=\"remove\" android:name=\"com.oculus.vr.focusaware\" />\n"; + } + manifest_activity_text += " </activity>\n"; + return manifest_activity_text; +} + +String _get_application_tag(const Ref<EditorExportPreset> &p_preset) { + String manifest_application_text = vformat( + " <application android:label=\"@string/godot_project_name_string\"\n" + " android:allowBackup=\"%s\"\n" + " android:icon=\"@mipmap/icon\"\n" + " android:isGame=\"%s\"\n" + " tools:replace=\"android:allowBackup,android:isGame\"\n" + " tools:ignore=\"GoogleAppIndexingWarning\">\n\n", + bool_to_string(p_preset->get("user_data_backup/allow")), + bool_to_string(p_preset->get("package/classify_as_game"))); + + manifest_application_text += _get_activity_tag(p_preset); + manifest_application_text += " </application>\n"; + return manifest_application_text; +} diff --git a/platform/android/export/gradle_export_util.h b/platform/android/export/gradle_export_util.h index 52a7e4c5cf..44e9a1727d 100644 --- a/platform/android/export/gradle_export_util.h +++ b/platform/android/export/gradle_export_util.h @@ -44,225 +44,43 @@ const String godot_project_name_xml_string = R"(<?xml version="1.0" encoding="ut </resources> )"; -int _get_android_orientation_value(DisplayServer::ScreenOrientation screen_orientation) { - switch (screen_orientation) { - case DisplayServer::SCREEN_PORTRAIT: - return 1; - case DisplayServer::SCREEN_REVERSE_LANDSCAPE: - return 8; - case DisplayServer::SCREEN_REVERSE_PORTRAIT: - return 9; - case DisplayServer::SCREEN_SENSOR_LANDSCAPE: - return 11; - case DisplayServer::SCREEN_SENSOR_PORTRAIT: - return 12; - case DisplayServer::SCREEN_SENSOR: - return 13; - case DisplayServer::SCREEN_LANDSCAPE: - default: - return 0; - } -} +int _get_android_orientation_value(DisplayServer::ScreenOrientation screen_orientation); -String _get_android_orientation_label(DisplayServer::ScreenOrientation screen_orientation) { - switch (screen_orientation) { - case DisplayServer::SCREEN_PORTRAIT: - return "portrait"; - case DisplayServer::SCREEN_REVERSE_LANDSCAPE: - return "reverseLandscape"; - case DisplayServer::SCREEN_REVERSE_PORTRAIT: - return "reversePortrait"; - case DisplayServer::SCREEN_SENSOR_LANDSCAPE: - return "userLandscape"; - case DisplayServer::SCREEN_SENSOR_PORTRAIT: - return "userPortrait"; - case DisplayServer::SCREEN_SENSOR: - return "fullUser"; - case DisplayServer::SCREEN_LANDSCAPE: - default: - return "landscape"; - } -} +String _get_android_orientation_label(DisplayServer::ScreenOrientation screen_orientation); // Utility method used to create a directory. -Error create_directory(const String &p_dir) { - if (!DirAccess::exists(p_dir)) { - DirAccess *filesystem_da = DirAccess::create(DirAccess::ACCESS_RESOURCES); - ERR_FAIL_COND_V_MSG(!filesystem_da, ERR_CANT_CREATE, "Cannot create directory '" + p_dir + "'."); - Error err = filesystem_da->make_dir_recursive(p_dir); - ERR_FAIL_COND_V_MSG(err, ERR_CANT_CREATE, "Cannot create directory '" + p_dir + "'."); - memdelete(filesystem_da); - } - return OK; -} +Error create_directory(const String &p_dir); // Writes p_data into a file at p_path, creating directories if necessary. // Note: this will overwrite the file at p_path if it already exists. -Error store_file_at_path(const String &p_path, const Vector<uint8_t> &p_data) { - String dir = p_path.get_base_dir(); - Error err = create_directory(dir); - if (err != OK) { - return err; - } - FileAccess *fa = FileAccess::open(p_path, FileAccess::WRITE); - ERR_FAIL_COND_V_MSG(!fa, ERR_CANT_CREATE, "Cannot create file '" + p_path + "'."); - fa->store_buffer(p_data.ptr(), p_data.size()); - memdelete(fa); - return OK; -} +Error store_file_at_path(const String &p_path, const Vector<uint8_t> &p_data); // Writes string p_data into a file at p_path, creating directories if necessary. // Note: this will overwrite the file at p_path if it already exists. -Error store_string_at_path(const String &p_path, const String &p_data) { - String dir = p_path.get_base_dir(); - Error err = create_directory(dir); - if (err != OK) { - if (OS::get_singleton()->is_stdout_verbose()) { - print_error("Unable to write data into " + p_path); - } - return err; - } - FileAccess *fa = FileAccess::open(p_path, FileAccess::WRITE); - ERR_FAIL_COND_V_MSG(!fa, ERR_CANT_CREATE, "Cannot create file '" + p_path + "'."); - fa->store_string(p_data); - memdelete(fa); - return OK; -} +Error store_string_at_path(const String &p_path, const String &p_data); // Implementation of EditorExportSaveFunction. // This method will only be called as an input to export_project_files. // It is used by the export_project_files method to save all the asset files into the gradle project. // It's functionality mirrors that of the method save_apk_file. // This method will be called ONLY when custom build is enabled. -Error rename_and_store_file_in_gradle_project(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key) { - String dst_path = p_path.replace_first("res://", "res://android/build/assets/"); - print_verbose("Saving project files from " + p_path + " into " + dst_path); - Error err = store_file_at_path(dst_path, p_data); - return err; -} +Error rename_and_store_file_in_gradle_project(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key); // Creates strings.xml files inside the gradle project for different locales. -Error _create_project_name_strings_files(const Ref<EditorExportPreset> &p_preset, const String &project_name) { - print_verbose("Creating strings resources for supported locales for project " + project_name); - // Stores the string into the default values directory. - String processed_default_xml_string = vformat(godot_project_name_xml_string, project_name.xml_escape(true)); - store_string_at_path("res://android/build/res/values/godot_project_name_string.xml", processed_default_xml_string); +Error _create_project_name_strings_files(const Ref<EditorExportPreset> &p_preset, const String &project_name); - // Searches the Gradle project res/ directory to find all supported locales - DirAccessRef da = DirAccess::open("res://android/build/res"); - if (!da) { - if (OS::get_singleton()->is_stdout_verbose()) { - print_error("Unable to open Android resources directory."); - } - return ERR_CANT_OPEN; - } - da->list_dir_begin(); - while (true) { - String file = da->get_next(); - if (file == "") { - break; - } - if (!file.begins_with("values-")) { - // NOTE: This assumes all directories that start with "values-" are for localization. - continue; - } - String locale = file.replace("values-", "").replace("-r", "_"); - String property_name = "application/config/name_" + locale; - String locale_directory = "res://android/build/res/" + file + "/godot_project_name_string.xml"; - if (ProjectSettings::get_singleton()->has_setting(property_name)) { - String locale_project_name = ProjectSettings::get_singleton()->get(property_name); - String processed_xml_string = vformat(godot_project_name_xml_string, locale_project_name.xml_escape(true)); - print_verbose("Storing project name for locale " + locale + " under " + locale_directory); - store_string_at_path(locale_directory, processed_xml_string); - } else { - // TODO: Once the legacy build system is deprecated we don't need to have xml files for this else branch - store_string_at_path(locale_directory, processed_default_xml_string); - } - } - da->list_dir_end(); - return OK; -} +String bool_to_string(bool v); -String bool_to_string(bool v) { - return v ? "true" : "false"; -} +String _get_gles_tag(); -String _get_gles_tag() { - bool min_gles3 = ProjectSettings::get_singleton()->get("rendering/driver/driver_name") == "GLES3" && - !ProjectSettings::get_singleton()->get("rendering/driver/fallback_to_gles2"); - return min_gles3 ? " <uses-feature android:glEsVersion=\"0x00030000\" android:required=\"true\" />\n" : ""; -} +String _get_screen_sizes_tag(const Ref<EditorExportPreset> &p_preset); -String _get_screen_sizes_tag(const Ref<EditorExportPreset> &p_preset) { - String manifest_screen_sizes = " <supports-screens \n tools:node=\"replace\""; - String sizes[] = { "small", "normal", "large", "xlarge" }; - size_t num_sizes = sizeof(sizes) / sizeof(sizes[0]); - for (size_t i = 0; i < num_sizes; i++) { - String feature_name = vformat("screen/support_%s", sizes[i]); - String feature_support = bool_to_string(p_preset->get(feature_name)); - String xml_entry = vformat("\n android:%sScreens=\"%s\"", sizes[i], feature_support); - manifest_screen_sizes += xml_entry; - } - manifest_screen_sizes += " />\n"; - return manifest_screen_sizes; -} +String _get_xr_features_tag(const Ref<EditorExportPreset> &p_preset); -String _get_xr_features_tag(const Ref<EditorExportPreset> &p_preset) { - String manifest_xr_features; - bool uses_xr = (int)(p_preset->get("xr_features/xr_mode")) == 1; - if (uses_xr) { - int hand_tracking_index = p_preset->get("xr_features/hand_tracking"); // 0: none, 1: optional, 2: required - if (hand_tracking_index == 1) { - manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"oculus.software.handtracking\" android:required=\"false\" />\n"; - } else if (hand_tracking_index == 2) { - manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"oculus.software.handtracking\" android:required=\"true\" />\n"; - } - } - return manifest_xr_features; -} +String _get_instrumentation_tag(const Ref<EditorExportPreset> &p_preset); -String _get_instrumentation_tag(const Ref<EditorExportPreset> &p_preset) { - String package_name = p_preset->get("package/unique_name"); - String manifest_instrumentation_text = vformat( - " <instrumentation\n" - " tools:node=\"replace\"\n" - " android:name=\".GodotInstrumentation\"\n" - " android:icon=\"@mipmap/icon\"\n" - " android:label=\"@string/godot_project_name_string\"\n" - " android:targetPackage=\"%s\" />\n", - package_name); - return manifest_instrumentation_text; -} +String _get_activity_tag(const Ref<EditorExportPreset> &p_preset); -String _get_activity_tag(const Ref<EditorExportPreset> &p_preset) { - bool uses_xr = (int)(p_preset->get("xr_features/xr_mode")) == 1; - String orientation = _get_android_orientation_label(DisplayServer::ScreenOrientation(int(GLOBAL_GET("display/window/handheld/orientation")))); - String manifest_activity_text = vformat( - " <activity android:name=\"com.godot.game.GodotApp\" " - "tools:replace=\"android:screenOrientation\" " - "android:screenOrientation=\"%s\">\n", - orientation); - if (!uses_xr) { - manifest_activity_text += " <meta-data tools:node=\"remove\" android:name=\"com.oculus.vr.focusaware\" />\n"; - } - manifest_activity_text += " </activity>\n"; - return manifest_activity_text; -} - -String _get_application_tag(const Ref<EditorExportPreset> &p_preset) { - String manifest_application_text = vformat( - " <application android:label=\"@string/godot_project_name_string\"\n" - " android:allowBackup=\"%s\"\n" - " android:icon=\"@mipmap/icon\"\n" - " android:isGame=\"%s\"\n" - " tools:replace=\"android:allowBackup,android:isGame\"\n" - " tools:ignore=\"GoogleAppIndexingWarning\">\n\n", - bool_to_string(p_preset->get("user_data_backup/allow")), - bool_to_string(p_preset->get("package/classify_as_game"))); - - manifest_application_text += _get_activity_tag(p_preset); - manifest_application_text += " </application>\n"; - return manifest_application_text; -} +String _get_application_tag(const Ref<EditorExportPreset> &p_preset); #endif //GODOT_GRADLE_EXPORT_UTIL_H diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.java b/platform/android/java/lib/src/org/godotengine/godot/Godot.java index 8ffa4a9249..01eb1f1ec8 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java +++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.java @@ -842,6 +842,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC } private void forceQuit() { + getActivity().finish(); System.exit(0); } diff --git a/platform/iphone/export/export.cpp b/platform/iphone/export/export.cpp index f21a14e84b..208626ae36 100644 --- a/platform/iphone/export/export.cpp +++ b/platform/iphone/export/export.cpp @@ -30,2027 +30,7 @@ #include "export.h" -#include "core/config/project_settings.h" -#include "core/io/file_access.h" -#include "core/io/image_loader.h" -#include "core/io/marshalls.h" -#include "core/io/resource_saver.h" -#include "core/io/zip_io.h" -#include "core/os/os.h" -#include "core/templates/safe_refcount.h" -#include "core/version.h" -#include "editor/editor_export.h" -#include "editor/editor_node.h" -#include "editor/editor_settings.h" -#include "main/splash.gen.h" -#include "platform/iphone/logo.gen.h" -#include "platform/iphone/plugin/godot_plugin_config.h" -#include "string.h" - -#include <sys/stat.h> - -class EditorExportPlatformIOS : public EditorExportPlatform { - GDCLASS(EditorExportPlatformIOS, EditorExportPlatform); - - int version_code; - - Ref<ImageTexture> logo; - - // Plugins - SafeFlag plugins_changed; - Thread check_for_changes_thread; - SafeFlag quit_request; - Mutex plugins_lock; - Vector<PluginConfigIOS> plugins; - - typedef Error (*FileHandler)(String p_file, void *p_userdata); - static Error _walk_dir_recursive(DirAccess *p_da, FileHandler p_handler, void *p_userdata); - static Error _codesign(String p_file, void *p_userdata); - void _blend_and_rotate(Ref<Image> &p_dst, Ref<Image> &p_src, bool p_rot); - - struct IOSConfigData { - String pkg_name; - String binary_name; - String plist_content; - String architectures; - String linker_flags; - String cpp_code; - String modules_buildfile; - String modules_fileref; - String modules_buildphase; - String modules_buildgrp; - Vector<String> capabilities; - }; - struct ExportArchitecture { - String name; - bool is_default = false; - - ExportArchitecture() {} - - ExportArchitecture(String p_name, bool p_is_default) { - name = p_name; - is_default = p_is_default; - } - }; - - struct IOSExportAsset { - String exported_path; - bool is_framework = false; // framework is anything linked to the binary, otherwise it's a resource - bool should_embed = false; - }; - - String _get_additional_plist_content(); - String _get_linker_flags(); - String _get_cpp_code(); - void _fix_config_file(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &pfile, const IOSConfigData &p_config, bool p_debug); - Error _export_loading_screen_images(const Ref<EditorExportPreset> &p_preset, const String &p_dest_dir); - Error _export_loading_screen_file(const Ref<EditorExportPreset> &p_preset, const String &p_dest_dir); - Error _export_icons(const Ref<EditorExportPreset> &p_preset, const String &p_iconset_dir); - - Vector<ExportArchitecture> _get_supported_architectures(); - Vector<String> _get_preset_architectures(const Ref<EditorExportPreset> &p_preset); - - void _add_assets_to_project(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_project_data, const Vector<IOSExportAsset> &p_additional_assets); - Error _export_additional_assets(const String &p_out_dir, const Vector<String> &p_assets, bool p_is_framework, bool p_should_embed, Vector<IOSExportAsset> &r_exported_assets); - Error _copy_asset(const String &p_out_dir, const String &p_asset, const String *p_custom_file_name, bool p_is_framework, bool p_should_embed, Vector<IOSExportAsset> &r_exported_assets); - Error _export_additional_assets(const String &p_out_dir, const Vector<SharedObject> &p_libraries, Vector<IOSExportAsset> &r_exported_assets); - Error _export_ios_plugins(const Ref<EditorExportPreset> &p_preset, IOSConfigData &p_config_data, const String &dest_dir, Vector<IOSExportAsset> &r_exported_assets, bool p_debug); - - bool is_package_name_valid(const String &p_package, String *r_error = nullptr) const { - String pname = p_package; - - if (pname.length() == 0) { - if (r_error) { - *r_error = TTR("Identifier is missing."); - } - return false; - } - - for (int i = 0; i < pname.length(); i++) { - char32_t c = pname[i]; - if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-' || c == '.')) { - if (r_error) { - *r_error = vformat(TTR("The character '%s' is not allowed in Identifier."), String::chr(c)); - } - return false; - } - } - - return true; - } - - static void _check_for_changes_poll_thread(void *ud) { - EditorExportPlatformIOS *ea = (EditorExportPlatformIOS *)ud; - - while (!ea->quit_request.is_set()) { - // Nothing to do if we already know the plugins have changed. - if (!ea->plugins_changed.is_set()) { - MutexLock lock(ea->plugins_lock); - - Vector<PluginConfigIOS> loaded_plugins = get_plugins(); - - if (ea->plugins.size() != loaded_plugins.size()) { - ea->plugins_changed.set(); - } else { - for (int i = 0; i < ea->plugins.size(); i++) { - if (ea->plugins[i].name != loaded_plugins[i].name || ea->plugins[i].last_updated != loaded_plugins[i].last_updated) { - ea->plugins_changed.set(); - break; - } - } - } - } - - uint64_t wait = 3000000; - uint64_t time = OS::get_singleton()->get_ticks_usec(); - while (OS::get_singleton()->get_ticks_usec() - time < wait) { - OS::get_singleton()->delay_usec(300000); - - if (ea->quit_request.is_set()) { - break; - } - } - } - } - -protected: - virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) override; - virtual void get_export_options(List<ExportOption> *r_options) override; - -public: - virtual String get_name() const override { return "iOS"; } - virtual String get_os_name() const override { return "iOS"; } - virtual Ref<Texture2D> get_logo() const override { return logo; } - - virtual bool should_update_export_options() override { - bool export_options_changed = plugins_changed.is_set(); - if (export_options_changed) { - // don't clear unless we're reporting true, to avoid race - plugins_changed.clear(); - } - return export_options_changed; - } - - virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override { - List<String> list; - list.push_back("ipa"); - return list; - } - virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override; - - virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override; - - virtual void get_platform_features(List<String> *r_features) override { - r_features->push_back("mobile"); - r_features->push_back("iOS"); - } - - virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) override { - } - - EditorExportPlatformIOS(); - ~EditorExportPlatformIOS(); - - /// List the gdip files in the directory specified by the p_path parameter. - static Vector<String> list_plugin_config_files(const String &p_path, bool p_check_directories) { - Vector<String> dir_files; - DirAccessRef da = DirAccess::open(p_path); - if (da) { - da->list_dir_begin(); - while (true) { - String file = da->get_next(); - if (file.is_empty()) { - break; - } - - if (file == "." || file == "..") { - continue; - } - - if (da->current_is_hidden()) { - continue; - } - - if (da->current_is_dir()) { - if (p_check_directories) { - Vector<String> directory_files = list_plugin_config_files(p_path.plus_file(file), false); - for (int i = 0; i < directory_files.size(); ++i) { - dir_files.push_back(file.plus_file(directory_files[i])); - } - } - - continue; - } - - if (file.ends_with(PluginConfigIOS::PLUGIN_CONFIG_EXT)) { - dir_files.push_back(file); - } - } - da->list_dir_end(); - } - - return dir_files; - } - - static Vector<PluginConfigIOS> get_plugins() { - Vector<PluginConfigIOS> loaded_plugins; - - String plugins_dir = ProjectSettings::get_singleton()->get_resource_path().plus_file("ios/plugins"); - - if (DirAccess::exists(plugins_dir)) { - Vector<String> plugins_filenames = list_plugin_config_files(plugins_dir, true); - - if (!plugins_filenames.is_empty()) { - Ref<ConfigFile> config_file = memnew(ConfigFile); - for (int i = 0; i < plugins_filenames.size(); i++) { - PluginConfigIOS config = load_plugin_config(config_file, plugins_dir.plus_file(plugins_filenames[i])); - if (config.valid_config) { - loaded_plugins.push_back(config); - } else { - print_error("Invalid plugin config file " + plugins_filenames[i]); - } - } - } - } - - return loaded_plugins; - } - - static Vector<PluginConfigIOS> get_enabled_plugins(const Ref<EditorExportPreset> &p_presets) { - Vector<PluginConfigIOS> enabled_plugins; - Vector<PluginConfigIOS> all_plugins = get_plugins(); - for (int i = 0; i < all_plugins.size(); i++) { - PluginConfigIOS plugin = all_plugins[i]; - bool enabled = p_presets->get("plugins/" + plugin.name); - if (enabled) { - enabled_plugins.push_back(plugin); - } - } - - return enabled_plugins; - } -}; - -void EditorExportPlatformIOS::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) { - String driver = ProjectSettings::get_singleton()->get("rendering/driver/driver_name"); - r_features->push_back("pvrtc"); - if (driver == "Vulkan") { - // FIXME: Review if this is correct. - r_features->push_back("etc2"); - } - - Vector<String> architectures = _get_preset_architectures(p_preset); - for (int i = 0; i < architectures.size(); ++i) { - r_features->push_back(architectures[i]); - } -} - -Vector<EditorExportPlatformIOS::ExportArchitecture> EditorExportPlatformIOS::_get_supported_architectures() { - Vector<ExportArchitecture> archs; - archs.push_back(ExportArchitecture("armv7", false)); // Disabled by default, not included in official templates. - archs.push_back(ExportArchitecture("arm64", true)); - return archs; -} - -struct LoadingScreenInfo { - const char *preset_key; - const char *export_name; - int width = 0; - int height = 0; - bool rotate = false; -}; - -static const LoadingScreenInfo loading_screen_infos[] = { - { "landscape_launch_screens/iphone_2436x1125", "Default-Landscape-X.png", 2436, 1125, false }, - { "landscape_launch_screens/iphone_2208x1242", "Default-Landscape-736h@3x.png", 2208, 1242, false }, - { "landscape_launch_screens/ipad_1024x768", "Default-Landscape.png", 1024, 768, false }, - { "landscape_launch_screens/ipad_2048x1536", "Default-Landscape@2x.png", 2048, 1536, false }, - - { "portrait_launch_screens/iphone_640x960", "Default-480h@2x.png", 640, 960, true }, - { "portrait_launch_screens/iphone_640x1136", "Default-568h@2x.png", 640, 1136, true }, - { "portrait_launch_screens/iphone_750x1334", "Default-667h@2x.png", 750, 1334, true }, - { "portrait_launch_screens/iphone_1125x2436", "Default-Portrait-X.png", 1125, 2436, true }, - { "portrait_launch_screens/ipad_768x1024", "Default-Portrait.png", 768, 1024, true }, - { "portrait_launch_screens/ipad_1536x2048", "Default-Portrait@2x.png", 1536, 2048, true }, - { "portrait_launch_screens/iphone_1242x2208", "Default-Portrait-736h@3x.png", 1242, 2208, true } -}; - -void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options) { - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), "")); - - Vector<ExportArchitecture> architectures = _get_supported_architectures(); - for (int i = 0; i < architectures.size(); ++i) { - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "architectures/" + architectures[i].name), architectures[i].is_default)); - } - - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/app_store_team_id"), "")); - - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/provisioning_profile_uuid_debug"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/code_sign_identity_debug", PROPERTY_HINT_PLACEHOLDER_TEXT, "iPhone Developer"), "iPhone Developer")); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/export_method_debug", PROPERTY_HINT_ENUM, "App Store,Development,Ad-Hoc,Enterprise"), 1)); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/provisioning_profile_uuid_release"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/code_sign_identity_release", PROPERTY_HINT_PLACEHOLDER_TEXT, "iPhone Distribution"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/export_method_release", PROPERTY_HINT_ENUM, "App Store,Development,Ad-Hoc,Enterprise"), 0)); - - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/targeted_device_family", PROPERTY_HINT_ENUM, "iPhone,iPad,iPhone & iPad"), 2)); - - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/info"), "Made with Godot Engine")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/bundle_identifier", PROPERTY_HINT_PLACEHOLDER_TEXT, "com.example.game"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/signature"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/short_version"), "1.0")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/version"), "1.0")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/copyright"), "")); - - Vector<PluginConfigIOS> found_plugins = get_plugins(); - for (int i = 0; i < found_plugins.size(); i++) { - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "plugins/" + found_plugins[i].name), false)); - } - - Set<String> plist_keys; - - for (int i = 0; i < found_plugins.size(); i++) { - // Editable plugin plist values - PluginConfigIOS plugin = found_plugins[i]; - const String *K = nullptr; - - while ((K = plugin.plist.next(K))) { - String key = *K; - PluginConfigIOS::PlistItem item = plugin.plist[key]; - switch (item.type) { - case PluginConfigIOS::PlistItemType::STRING_INPUT: { - String preset_name = "plugins_plist/" + key; - if (!plist_keys.has(preset_name)) { - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, preset_name), item.value)); - plist_keys.insert(preset_name); - } - } break; - default: - continue; - } - } - } - - plugins_changed.clear(); - plugins = found_plugins; - - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/access_wifi"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/push_notifications"), false)); - - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "user_data/accessible_from_files_app"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "user_data/accessible_from_itunes_sharing"), false)); - - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/camera_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the camera"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/microphone_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the microphone"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/photolibrary_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need access to the photo library"), "")); - - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "icons/generate_missing"), false)); - - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "required_icons/iphone_120x120", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPhone/iPod Touch with retina display - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "required_icons/ipad_76x76", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPad - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "required_icons/app_store_1024x1024", PROPERTY_HINT_FILE, "*.png"), "")); // App Store - - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/iphone_180x180", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPhone with retina HD display - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/ipad_152x152", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPad with retina display - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/ipad_167x167", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPad Pro - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/spotlight_40x40", PROPERTY_HINT_FILE, "*.png"), "")); // Spotlight - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/spotlight_80x80", PROPERTY_HINT_FILE, "*.png"), "")); // Spotlight on devices with retina display - - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "storyboard/use_launch_screen_storyboard"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "storyboard/image_scale_mode", PROPERTY_HINT_ENUM, "Same as Logo,Center,Scale to Fit,Scale to Fill,Scale"), 0)); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "storyboard/custom_image@2x", PROPERTY_HINT_FILE, "*.png"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "storyboard/custom_image@3x", PROPERTY_HINT_FILE, "*.png"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "storyboard/use_custom_bg_color"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::COLOR, "storyboard/custom_bg_color"), Color())); - - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "launch_screens/generate_missing"), false)); - - for (uint64_t i = 0; i < sizeof(loading_screen_infos) / sizeof(loading_screen_infos[0]); ++i) { - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, loading_screen_infos[i].preset_key, PROPERTY_HINT_FILE, "*.png"), "")); - } -} - -void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &pfile, const IOSConfigData &p_config, bool p_debug) { - static const String export_method_string[] = { - "app-store", - "development", - "ad-hoc", - "enterprise" - }; - static const String storyboard_image_scale_mode[] = { - "center", - "scaleAspectFit", - "scaleAspectFill", - "scaleToFill" - }; - String str; - String strnew; - str.parse_utf8((const char *)pfile.ptr(), pfile.size()); - Vector<String> lines = str.split("\n"); - for (int i = 0; i < lines.size(); i++) { - if (lines[i].find("$binary") != -1) { - strnew += lines[i].replace("$binary", p_config.binary_name) + "\n"; - } else if (lines[i].find("$modules_buildfile") != -1) { - strnew += lines[i].replace("$modules_buildfile", p_config.modules_buildfile) + "\n"; - } else if (lines[i].find("$modules_fileref") != -1) { - strnew += lines[i].replace("$modules_fileref", p_config.modules_fileref) + "\n"; - } else if (lines[i].find("$modules_buildphase") != -1) { - strnew += lines[i].replace("$modules_buildphase", p_config.modules_buildphase) + "\n"; - } else if (lines[i].find("$modules_buildgrp") != -1) { - strnew += lines[i].replace("$modules_buildgrp", p_config.modules_buildgrp) + "\n"; - } else if (lines[i].find("$name") != -1) { - strnew += lines[i].replace("$name", p_config.pkg_name) + "\n"; - } else if (lines[i].find("$info") != -1) { - strnew += lines[i].replace("$info", p_preset->get("application/info")) + "\n"; - } else if (lines[i].find("$bundle_identifier") != -1) { - strnew += lines[i].replace("$bundle_identifier", p_preset->get("application/bundle_identifier")) + "\n"; - } else if (lines[i].find("$short_version") != -1) { - strnew += lines[i].replace("$short_version", p_preset->get("application/short_version")) + "\n"; - } else if (lines[i].find("$version") != -1) { - strnew += lines[i].replace("$version", p_preset->get("application/version")) + "\n"; - } else if (lines[i].find("$signature") != -1) { - strnew += lines[i].replace("$signature", p_preset->get("application/signature")) + "\n"; - } else if (lines[i].find("$copyright") != -1) { - strnew += lines[i].replace("$copyright", p_preset->get("application/copyright")) + "\n"; - } else if (lines[i].find("$team_id") != -1) { - strnew += lines[i].replace("$team_id", p_preset->get("application/app_store_team_id")) + "\n"; - } else if (lines[i].find("$default_build_config") != -1) { - strnew += lines[i].replace("$default_build_config", p_debug ? "Debug" : "Release") + "\n"; - } else if (lines[i].find("$export_method") != -1) { - int export_method = p_preset->get(p_debug ? "application/export_method_debug" : "application/export_method_release"); - strnew += lines[i].replace("$export_method", export_method_string[export_method]) + "\n"; - } else if (lines[i].find("$provisioning_profile_uuid_release") != -1) { - strnew += lines[i].replace("$provisioning_profile_uuid_release", p_preset->get("application/provisioning_profile_uuid_release")) + "\n"; - } else if (lines[i].find("$provisioning_profile_uuid_debug") != -1) { - strnew += lines[i].replace("$provisioning_profile_uuid_debug", p_preset->get("application/provisioning_profile_uuid_debug")) + "\n"; - } else if (lines[i].find("$provisioning_profile_uuid") != -1) { - String uuid = p_debug ? p_preset->get("application/provisioning_profile_uuid_debug") : p_preset->get("application/provisioning_profile_uuid_release"); - strnew += lines[i].replace("$provisioning_profile_uuid", uuid) + "\n"; - } else if (lines[i].find("$code_sign_identity_debug") != -1) { - strnew += lines[i].replace("$code_sign_identity_debug", p_preset->get("application/code_sign_identity_debug")) + "\n"; - } else if (lines[i].find("$code_sign_identity_release") != -1) { - strnew += lines[i].replace("$code_sign_identity_release", p_preset->get("application/code_sign_identity_release")) + "\n"; - } else if (lines[i].find("$additional_plist_content") != -1) { - strnew += lines[i].replace("$additional_plist_content", p_config.plist_content) + "\n"; - } else if (lines[i].find("$godot_archs") != -1) { - strnew += lines[i].replace("$godot_archs", p_config.architectures) + "\n"; - } else if (lines[i].find("$linker_flags") != -1) { - strnew += lines[i].replace("$linker_flags", p_config.linker_flags) + "\n"; - } else if (lines[i].find("$targeted_device_family") != -1) { - String xcode_value; - switch ((int)p_preset->get("application/targeted_device_family")) { - case 0: // iPhone - xcode_value = "1"; - break; - case 1: // iPad - xcode_value = "2"; - break; - case 2: // iPhone & iPad - xcode_value = "1,2"; - break; - } - strnew += lines[i].replace("$targeted_device_family", xcode_value) + "\n"; - } else if (lines[i].find("$cpp_code") != -1) { - strnew += lines[i].replace("$cpp_code", p_config.cpp_code) + "\n"; - } else if (lines[i].find("$docs_in_place") != -1) { - strnew += lines[i].replace("$docs_in_place", ((bool)p_preset->get("user_data/accessible_from_files_app")) ? "<true/>" : "<false/>") + "\n"; - } else if (lines[i].find("$docs_sharing") != -1) { - strnew += lines[i].replace("$docs_sharing", ((bool)p_preset->get("user_data/accessible_from_itunes_sharing")) ? "<true/>" : "<false/>") + "\n"; - } else if (lines[i].find("$entitlements_push_notifications") != -1) { - bool is_on = p_preset->get("capabilities/push_notifications"); - strnew += lines[i].replace("$entitlements_push_notifications", is_on ? "<key>aps-environment</key><string>development</string>" : "") + "\n"; - } else if (lines[i].find("$required_device_capabilities") != -1) { - String capabilities; - - // I've removed armv7 as we can run on 64bit only devices - // Note that capabilities listed here are requirements for the app to be installed. - // They don't enable anything. - Vector<String> capabilities_list = p_config.capabilities; - - if ((bool)p_preset->get("capabilities/access_wifi") && !capabilities_list.has("wifi")) { - capabilities_list.push_back("wifi"); - } - - for (int idx = 0; idx < capabilities_list.size(); idx++) { - capabilities += "<string>" + capabilities_list[idx] + "</string>\n"; - } - - strnew += lines[i].replace("$required_device_capabilities", capabilities); - } else if (lines[i].find("$interface_orientations") != -1) { - String orientations; - const DisplayServer::ScreenOrientation screen_orientation = - DisplayServer::ScreenOrientation(int(GLOBAL_GET("display/window/handheld/orientation"))); - - switch (screen_orientation) { - case DisplayServer::SCREEN_LANDSCAPE: - orientations += "<string>UIInterfaceOrientationLandscapeLeft</string>\n"; - break; - case DisplayServer::SCREEN_PORTRAIT: - orientations += "<string>UIInterfaceOrientationPortrait</string>\n"; - break; - case DisplayServer::SCREEN_REVERSE_LANDSCAPE: - orientations += "<string>UIInterfaceOrientationLandscapeRight</string>\n"; - break; - case DisplayServer::SCREEN_REVERSE_PORTRAIT: - orientations += "<string>UIInterfaceOrientationPortraitUpsideDown</string>\n"; - break; - case DisplayServer::SCREEN_SENSOR_LANDSCAPE: - // Allow both landscape orientations depending on sensor direction. - orientations += "<string>UIInterfaceOrientationLandscapeLeft</string>\n"; - orientations += "<string>UIInterfaceOrientationLandscapeRight</string>\n"; - break; - case DisplayServer::SCREEN_SENSOR_PORTRAIT: - // Allow both portrait orientations depending on sensor direction. - orientations += "<string>UIInterfaceOrientationPortrait</string>\n"; - orientations += "<string>UIInterfaceOrientationPortraitUpsideDown</string>\n"; - break; - case DisplayServer::SCREEN_SENSOR: - // Allow all screen orientations depending on sensor direction. - orientations += "<string>UIInterfaceOrientationLandscapeLeft</string>\n"; - orientations += "<string>UIInterfaceOrientationLandscapeRight</string>\n"; - orientations += "<string>UIInterfaceOrientationPortrait</string>\n"; - orientations += "<string>UIInterfaceOrientationPortraitUpsideDown</string>\n"; - break; - } - - strnew += lines[i].replace("$interface_orientations", orientations); - } else if (lines[i].find("$camera_usage_description") != -1) { - String description = p_preset->get("privacy/camera_usage_description"); - strnew += lines[i].replace("$camera_usage_description", description) + "\n"; - } else if (lines[i].find("$microphone_usage_description") != -1) { - String description = p_preset->get("privacy/microphone_usage_description"); - strnew += lines[i].replace("$microphone_usage_description", description) + "\n"; - } else if (lines[i].find("$photolibrary_usage_description") != -1) { - String description = p_preset->get("privacy/photolibrary_usage_description"); - strnew += lines[i].replace("$photolibrary_usage_description", description) + "\n"; - } else if (lines[i].find("$plist_launch_screen_name") != -1) { - bool is_on = p_preset->get("storyboard/use_launch_screen_storyboard"); - String value = is_on ? "<key>UILaunchStoryboardName</key>\n<string>Launch Screen</string>" : ""; - strnew += lines[i].replace("$plist_launch_screen_name", value) + "\n"; - } else if (lines[i].find("$pbx_launch_screen_file_reference") != -1) { - bool is_on = p_preset->get("storyboard/use_launch_screen_storyboard"); - String value = is_on ? "90DD2D9D24B36E8000717FE1 = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = \"Launch Screen.storyboard\"; sourceTree = \"<group>\"; };" : ""; - strnew += lines[i].replace("$pbx_launch_screen_file_reference", value) + "\n"; - } else if (lines[i].find("$pbx_launch_screen_copy_files") != -1) { - bool is_on = p_preset->get("storyboard/use_launch_screen_storyboard"); - String value = is_on ? "90DD2D9D24B36E8000717FE1 /* Launch Screen.storyboard */," : ""; - strnew += lines[i].replace("$pbx_launch_screen_copy_files", value) + "\n"; - } else if (lines[i].find("$pbx_launch_screen_build_phase") != -1) { - bool is_on = p_preset->get("storyboard/use_launch_screen_storyboard"); - String value = is_on ? "90DD2D9E24B36E8000717FE1 /* Launch Screen.storyboard in Resources */," : ""; - strnew += lines[i].replace("$pbx_launch_screen_build_phase", value) + "\n"; - } else if (lines[i].find("$pbx_launch_screen_build_reference") != -1) { - bool is_on = p_preset->get("storyboard/use_launch_screen_storyboard"); - String value = is_on ? "90DD2D9E24B36E8000717FE1 /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 90DD2D9D24B36E8000717FE1 /* Launch Screen.storyboard */; };" : ""; - strnew += lines[i].replace("$pbx_launch_screen_build_reference", value) + "\n"; - } else if (lines[i].find("$pbx_launch_image_usage_setting") != -1) { - bool is_on = p_preset->get("storyboard/use_launch_screen_storyboard"); - String value = is_on ? "" : "ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;"; - strnew += lines[i].replace("$pbx_launch_image_usage_setting", value) + "\n"; - } else if (lines[i].find("$launch_screen_image_mode") != -1) { - int image_scale_mode = p_preset->get("storyboard/image_scale_mode"); - String value; - - switch (image_scale_mode) { - case 0: { - String logo_path = ProjectSettings::get_singleton()->get("application/boot_splash/image"); - bool is_on = ProjectSettings::get_singleton()->get("application/boot_splash/fullsize"); - // If custom logo is not specified, Godot does not scale default one, so we should do the same. - value = (is_on && logo_path.length() > 0) ? "scaleAspectFit" : "center"; - } break; - default: { - value = storyboard_image_scale_mode[image_scale_mode - 1]; - } - } - - strnew += lines[i].replace("$launch_screen_image_mode", value) + "\n"; - } else if (lines[i].find("$launch_screen_background_color") != -1) { - bool use_custom = p_preset->get("storyboard/use_custom_bg_color"); - Color color = use_custom ? p_preset->get("storyboard/custom_bg_color") : ProjectSettings::get_singleton()->get("application/boot_splash/bg_color"); - const String value_format = "red=\"$red\" green=\"$green\" blue=\"$blue\" alpha=\"$alpha\""; - - Dictionary value_dictionary; - value_dictionary["red"] = color.r; - value_dictionary["green"] = color.g; - value_dictionary["blue"] = color.b; - value_dictionary["alpha"] = color.a; - String value = value_format.format(value_dictionary, "$_"); - - strnew += lines[i].replace("$launch_screen_background_color", value) + "\n"; - } else { - strnew += lines[i] + "\n"; - } - } - - // !BAS! I'm assuming the 9 in the original code was a typo. I've added -1 or else it seems to also be adding our terminating zero... - // should apply the same fix in our OSX export. - CharString cs = strnew.utf8(); - pfile.resize(cs.size() - 1); - for (int i = 0; i < cs.size() - 1; i++) { - pfile.write[i] = cs[i]; - } -} - -String EditorExportPlatformIOS::_get_additional_plist_content() { - Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins(); - String result; - for (int i = 0; i < export_plugins.size(); ++i) { - result += export_plugins[i]->get_ios_plist_content(); - } - return result; -} - -String EditorExportPlatformIOS::_get_linker_flags() { - Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins(); - String result; - for (int i = 0; i < export_plugins.size(); ++i) { - String flags = export_plugins[i]->get_ios_linker_flags(); - if (flags.length() == 0) { - continue; - } - if (result.length() > 0) { - result += ' '; - } - result += flags; - } - // the flags will be enclosed in quotes, so need to escape them - return result.replace("\"", "\\\""); -} - -String EditorExportPlatformIOS::_get_cpp_code() { - Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins(); - String result; - for (int i = 0; i < export_plugins.size(); ++i) { - result += export_plugins[i]->get_ios_cpp_code(); - } - return result; -} - -void EditorExportPlatformIOS::_blend_and_rotate(Ref<Image> &p_dst, Ref<Image> &p_src, bool p_rot) { - ERR_FAIL_COND(p_dst.is_null()); - ERR_FAIL_COND(p_src.is_null()); - - int sw = p_rot ? p_src->get_height() : p_src->get_width(); - int sh = p_rot ? p_src->get_width() : p_src->get_height(); - - int x_pos = (p_dst->get_width() - sw) / 2; - int y_pos = (p_dst->get_height() - sh) / 2; - - int xs = (x_pos >= 0) ? 0 : -x_pos; - int ys = (y_pos >= 0) ? 0 : -y_pos; - - if (sw + x_pos > p_dst->get_width()) { - sw = p_dst->get_width() - x_pos; - } - if (sh + y_pos > p_dst->get_height()) { - sh = p_dst->get_height() - y_pos; - } - - for (int y = ys; y < sh; y++) { - for (int x = xs; x < sw; x++) { - Color sc = p_rot ? p_src->get_pixel(p_src->get_width() - y - 1, x) : p_src->get_pixel(x, y); - Color dc = p_dst->get_pixel(x_pos + x, y_pos + y); - dc.r = (double)(sc.a * sc.r + dc.a * (1.0 - sc.a) * dc.r); - dc.g = (double)(sc.a * sc.g + dc.a * (1.0 - sc.a) * dc.g); - dc.b = (double)(sc.a * sc.b + dc.a * (1.0 - sc.a) * dc.b); - dc.a = (double)(sc.a + dc.a * (1.0 - sc.a)); - p_dst->set_pixel(x_pos + x, y_pos + y, dc); - } - } -} - -struct IconInfo { - const char *preset_key; - const char *idiom; - const char *export_name; - const char *actual_size_side; - const char *scale; - const char *unscaled_size; - bool is_required = false; -}; - -static const IconInfo icon_infos[] = { - { "required_icons/iphone_120x120", "iphone", "Icon-120.png", "120", "2x", "60x60", true }, - { "required_icons/iphone_120x120", "iphone", "Icon-120.png", "120", "3x", "40x40", true }, - - { "required_icons/ipad_76x76", "ipad", "Icon-76.png", "76", "1x", "76x76", true }, - { "required_icons/app_store_1024x1024", "ios-marketing", "Icon-1024.png", "1024", "1x", "1024x1024", true }, - - { "optional_icons/iphone_180x180", "iphone", "Icon-180.png", "180", "3x", "60x60", false }, - - { "optional_icons/ipad_152x152", "ipad", "Icon-152.png", "152", "2x", "76x76", false }, - - { "optional_icons/ipad_167x167", "ipad", "Icon-167.png", "167", "2x", "83.5x83.5", false }, - - { "optional_icons/spotlight_40x40", "ipad", "Icon-40.png", "40", "1x", "40x40", false }, - - { "optional_icons/spotlight_80x80", "iphone", "Icon-80.png", "80", "2x", "40x40", false }, - { "optional_icons/spotlight_80x80", "ipad", "Icon-80.png", "80", "2x", "40x40", false } -}; - -Error EditorExportPlatformIOS::_export_icons(const Ref<EditorExportPreset> &p_preset, const String &p_iconset_dir) { - String json_description = "{\"images\":["; - String sizes; - - DirAccess *da = DirAccess::open(p_iconset_dir); - ERR_FAIL_COND_V_MSG(!da, ERR_CANT_OPEN, "Cannot open directory '" + p_iconset_dir + "'."); - - for (uint64_t i = 0; i < (sizeof(icon_infos) / sizeof(icon_infos[0])); ++i) { - IconInfo info = icon_infos[i]; - int side_size = String(info.actual_size_side).to_int(); - String icon_path = p_preset->get(info.preset_key); - if (icon_path.length() == 0) { - if ((bool)p_preset->get("icons/generate_missing")) { - // Resize main app icon - icon_path = ProjectSettings::get_singleton()->get("application/config/icon"); - Ref<Image> img = memnew(Image); - Error err = ImageLoader::load_image(icon_path, img); - if (err != OK) { - ERR_PRINT("Invalid icon (" + String(info.preset_key) + "): '" + icon_path + "'."); - return ERR_UNCONFIGURED; - } - img->resize(side_size, side_size); - err = img->save_png(p_iconset_dir + info.export_name); - if (err) { - String err_str = String("Failed to export icon(" + String(info.preset_key) + "): '" + icon_path + "'."); - ERR_PRINT(err_str.utf8().get_data()); - return err; - } - } else { - if (info.is_required) { - String err_str = String("Required icon (") + info.preset_key + ") is not specified in the preset."; - ERR_PRINT(err_str); - return ERR_UNCONFIGURED; - } else { - String err_str = String("Icon (") + info.preset_key + ") is not specified in the preset."; - WARN_PRINT(err_str); - } - continue; - } - } else { - // Load custom icon - Ref<Image> img = memnew(Image); - Error err = ImageLoader::load_image(icon_path, img); - if (err != OK) { - ERR_PRINT("Invalid icon (" + String(info.preset_key) + "): '" + icon_path + "'."); - return ERR_UNCONFIGURED; - } - if (img->get_width() != side_size || img->get_height() != side_size) { - ERR_PRINT("Invalid icon size (" + String(info.preset_key) + "): '" + icon_path + "'."); - return ERR_UNCONFIGURED; - } - - err = da->copy(icon_path, p_iconset_dir + info.export_name); - if (err) { - memdelete(da); - String err_str = String("Failed to export icon(" + String(info.preset_key) + "): '" + icon_path + "'."); - ERR_PRINT(err_str.utf8().get_data()); - return err; - } - } - sizes += String(info.actual_size_side) + "\n"; - if (i > 0) { - json_description += ","; - } - json_description += String("{"); - json_description += String("\"idiom\":") + "\"" + info.idiom + "\","; - json_description += String("\"size\":") + "\"" + info.unscaled_size + "\","; - json_description += String("\"scale\":") + "\"" + info.scale + "\","; - json_description += String("\"filename\":") + "\"" + info.export_name + "\""; - json_description += String("}"); - } - json_description += "]}"; - memdelete(da); - - FileAccess *json_file = FileAccess::open(p_iconset_dir + "Contents.json", FileAccess::WRITE); - ERR_FAIL_COND_V(!json_file, ERR_CANT_CREATE); - CharString json_utf8 = json_description.utf8(); - json_file->store_buffer((const uint8_t *)json_utf8.get_data(), json_utf8.length()); - memdelete(json_file); - - FileAccess *sizes_file = FileAccess::open(p_iconset_dir + "sizes", FileAccess::WRITE); - ERR_FAIL_COND_V(!sizes_file, ERR_CANT_CREATE); - CharString sizes_utf8 = sizes.utf8(); - sizes_file->store_buffer((const uint8_t *)sizes_utf8.get_data(), sizes_utf8.length()); - memdelete(sizes_file); - - return OK; -} - -Error EditorExportPlatformIOS::_export_loading_screen_file(const Ref<EditorExportPreset> &p_preset, const String &p_dest_dir) { - const String custom_launch_image_2x = p_preset->get("storyboard/custom_image@2x"); - const String custom_launch_image_3x = p_preset->get("storyboard/custom_image@3x"); - - if (custom_launch_image_2x.length() > 0 && custom_launch_image_3x.length() > 0) { - Ref<Image> image; - String image_path = p_dest_dir.plus_file("splash@2x.png"); - image.instantiate(); - Error err = image->load(custom_launch_image_2x); - - if (err) { - image.unref(); - return err; - } - - if (image->save_png(image_path) != OK) { - return ERR_FILE_CANT_WRITE; - } - - image.unref(); - image_path = p_dest_dir.plus_file("splash@3x.png"); - image.instantiate(); - err = image->load(custom_launch_image_3x); - - if (err) { - image.unref(); - return err; - } - - if (image->save_png(image_path) != OK) { - return ERR_FILE_CANT_WRITE; - } - } else { - Ref<Image> splash; - - const String splash_path = ProjectSettings::get_singleton()->get("application/boot_splash/image"); - - if (!splash_path.is_empty()) { - splash.instantiate(); - const Error err = splash->load(splash_path); - if (err) { - splash.unref(); - } - } - - if (splash.is_null()) { - splash = Ref<Image>(memnew(Image(boot_splash_png))); - } - - // Using same image for both @2x and @3x - // because Godot's own boot logo uses single image for all resolutions. - // Also not using @1x image, because devices using this image variant - // are not supported by iOS 9, which is minimal target. - const String splash_png_path_2x = p_dest_dir.plus_file("splash@2x.png"); - const String splash_png_path_3x = p_dest_dir.plus_file("splash@3x.png"); - - if (splash->save_png(splash_png_path_2x) != OK) { - return ERR_FILE_CANT_WRITE; - } - - if (splash->save_png(splash_png_path_3x) != OK) { - return ERR_FILE_CANT_WRITE; - } - } - - return OK; -} - -Error EditorExportPlatformIOS::_export_loading_screen_images(const Ref<EditorExportPreset> &p_preset, const String &p_dest_dir) { - DirAccess *da = DirAccess::open(p_dest_dir); - ERR_FAIL_COND_V_MSG(!da, ERR_CANT_OPEN, "Cannot open directory '" + p_dest_dir + "'."); - - for (uint64_t i = 0; i < sizeof(loading_screen_infos) / sizeof(loading_screen_infos[0]); ++i) { - LoadingScreenInfo info = loading_screen_infos[i]; - String loading_screen_file = p_preset->get(info.preset_key); - if (loading_screen_file.size() > 0) { - // Load custom loading screens - Ref<Image> img = memnew(Image); - Error err = ImageLoader::load_image(loading_screen_file, img); - if (err != OK) { - ERR_PRINT("Invalid loading screen (" + String(info.preset_key) + "): '" + loading_screen_file + "'."); - return ERR_UNCONFIGURED; - } - if (img->get_width() != info.width || img->get_height() != info.height) { - ERR_PRINT("Invalid loading screen size (" + String(info.preset_key) + "): '" + loading_screen_file + "'."); - return ERR_UNCONFIGURED; - } - err = da->copy(loading_screen_file, p_dest_dir + info.export_name); - if (err) { - memdelete(da); - String err_str = String("Failed to export loading screen (") + info.preset_key + ") from path '" + loading_screen_file + "'."; - ERR_PRINT(err_str.utf8().get_data()); - return err; - } - } else if ((bool)p_preset->get("launch_screens/generate_missing")) { - // Generate loading screen from the splash screen - Color boot_bg_color = ProjectSettings::get_singleton()->get("application/boot_splash/bg_color"); - String boot_logo_path = ProjectSettings::get_singleton()->get("application/boot_splash/image"); - bool boot_logo_scale = ProjectSettings::get_singleton()->get("application/boot_splash/fullsize"); - - Ref<Image> img = memnew(Image); - img->create(info.width, info.height, false, Image::FORMAT_RGBA8); - img->fill(boot_bg_color); - - Ref<Image> img_bs; - - if (boot_logo_path.length() > 0) { - img_bs = Ref<Image>(memnew(Image)); - ImageLoader::load_image(boot_logo_path, img_bs); - } - if (!img_bs.is_valid()) { - img_bs = Ref<Image>(memnew(Image(boot_splash_png))); - } - if (img_bs.is_valid()) { - float aspect_ratio = (float)img_bs->get_width() / (float)img_bs->get_height(); - if (info.rotate) { - if (boot_logo_scale) { - if (info.width * aspect_ratio <= info.height) { - img_bs->resize(info.width * aspect_ratio, info.width); - } else { - img_bs->resize(info.height, info.height / aspect_ratio); - } - } - } else { - if (boot_logo_scale) { - if (info.height * aspect_ratio <= info.width) { - img_bs->resize(info.height * aspect_ratio, info.height); - } else { - img_bs->resize(info.width, info.width / aspect_ratio); - } - } - } - _blend_and_rotate(img, img_bs, info.rotate); - } - Error err = img->save_png(p_dest_dir + info.export_name); - if (err) { - String err_str = String("Failed to export loading screen (") + info.preset_key + ") from splash screen."; - WARN_PRINT(err_str.utf8().get_data()); - } - } else { - String err_str = String("No loading screen (") + info.preset_key + ") specified."; - WARN_PRINT(err_str.utf8().get_data()); - } - } - memdelete(da); - - return OK; -} - -Error EditorExportPlatformIOS::_walk_dir_recursive(DirAccess *p_da, FileHandler p_handler, void *p_userdata) { - Vector<String> dirs; - String path; - String current_dir = p_da->get_current_dir(); - p_da->list_dir_begin(); - while ((path = p_da->get_next()).length() != 0) { - if (p_da->current_is_dir()) { - if (path != "." && path != "..") { - dirs.push_back(path); - } - } else { - Error err = p_handler(current_dir.plus_file(path), p_userdata); - if (err) { - p_da->list_dir_end(); - return err; - } - } - } - p_da->list_dir_end(); - - for (int i = 0; i < dirs.size(); ++i) { - String dir = dirs[i]; - p_da->change_dir(dir); - Error err = _walk_dir_recursive(p_da, p_handler, p_userdata); - p_da->change_dir(".."); - if (err) { - return err; - } - } - - return OK; -} - -struct CodesignData { - const Ref<EditorExportPreset> &preset; - bool debug = false; - - CodesignData(const Ref<EditorExportPreset> &p_preset, bool p_debug) : - preset(p_preset), - debug(p_debug) { - } -}; - -Error EditorExportPlatformIOS::_codesign(String p_file, void *p_userdata) { - if (p_file.ends_with(".dylib")) { - CodesignData *data = (CodesignData *)p_userdata; - print_line(String("Signing ") + p_file); - List<String> codesign_args; - codesign_args.push_back("-f"); - codesign_args.push_back("-s"); - codesign_args.push_back(data->preset->get(data->debug ? "application/code_sign_identity_debug" : "application/code_sign_identity_release")); - codesign_args.push_back(p_file); - return OS::get_singleton()->execute("codesign", codesign_args); - } - return OK; -} - -struct PbxId { -private: - static char _hex_char(uint8_t four_bits) { - if (four_bits < 10) { - return ('0' + four_bits); - } - return 'A' + (four_bits - 10); - } - - static String _hex_pad(uint32_t num) { - Vector<char> ret; - ret.resize(sizeof(num) * 2); - for (uint64_t i = 0; i < sizeof(num) * 2; ++i) { - uint8_t four_bits = (num >> (sizeof(num) * 8 - (i + 1) * 4)) & 0xF; - ret.write[i] = _hex_char(four_bits); - } - return String::utf8(ret.ptr(), ret.size()); - } - -public: - uint32_t high_bits; - uint32_t mid_bits; - uint32_t low_bits; - - String str() const { - return _hex_pad(high_bits) + _hex_pad(mid_bits) + _hex_pad(low_bits); - } - - PbxId &operator++() { - low_bits++; - if (!low_bits) { - mid_bits++; - if (!mid_bits) { - high_bits++; - } - } - - return *this; - } -}; - -struct ExportLibsData { - Vector<String> lib_paths; - String dest_dir; -}; - -void EditorExportPlatformIOS::_add_assets_to_project(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_project_data, const Vector<IOSExportAsset> &p_additional_assets) { - // that is just a random number, we just need Godot IDs not to clash with - // existing IDs in the project. - PbxId current_id = { 0x58938401, 0, 0 }; - String pbx_files; - String pbx_frameworks_build; - String pbx_frameworks_refs; - String pbx_resources_build; - String pbx_resources_refs; - String pbx_embeded_frameworks; - - const String file_info_format = String("$build_id = {isa = PBXBuildFile; fileRef = $ref_id; };\n") + - "$ref_id = {isa = PBXFileReference; lastKnownFileType = $file_type; name = \"$name\"; path = \"$file_path\"; sourceTree = \"<group>\"; };\n"; - - for (int i = 0; i < p_additional_assets.size(); ++i) { - String additional_asset_info_format = file_info_format; - - String build_id = (++current_id).str(); - String ref_id = (++current_id).str(); - String framework_id = ""; - - const IOSExportAsset &asset = p_additional_assets[i]; - - String type; - if (asset.exported_path.ends_with(".framework")) { - if (asset.should_embed) { - additional_asset_info_format += "$framework_id = {isa = PBXBuildFile; fileRef = $ref_id; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };\n"; - framework_id = (++current_id).str(); - pbx_embeded_frameworks += framework_id + ",\n"; - } - - type = "wrapper.framework"; - } else if (asset.exported_path.ends_with(".xcframework")) { - if (asset.should_embed) { - additional_asset_info_format += "$framework_id = {isa = PBXBuildFile; fileRef = $ref_id; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };\n"; - framework_id = (++current_id).str(); - pbx_embeded_frameworks += framework_id + ",\n"; - } - - type = "wrapper.xcframework"; - } else if (asset.exported_path.ends_with(".dylib")) { - type = "compiled.mach-o.dylib"; - } else if (asset.exported_path.ends_with(".a")) { - type = "archive.ar"; - } else { - type = "file"; - } - - String &pbx_build = asset.is_framework ? pbx_frameworks_build : pbx_resources_build; - String &pbx_refs = asset.is_framework ? pbx_frameworks_refs : pbx_resources_refs; - - if (pbx_build.length() > 0) { - pbx_build += ",\n"; - pbx_refs += ",\n"; - } - pbx_build += build_id; - pbx_refs += ref_id; - - Dictionary format_dict; - format_dict["build_id"] = build_id; - format_dict["ref_id"] = ref_id; - format_dict["name"] = asset.exported_path.get_file(); - format_dict["file_path"] = asset.exported_path; - format_dict["file_type"] = type; - if (framework_id.length() > 0) { - format_dict["framework_id"] = framework_id; - } - pbx_files += additional_asset_info_format.format(format_dict, "$_"); - } - - // Note, frameworks like gamekit are always included in our project.pbxprof file - // even if turned off in capabilities. - - String str = String::utf8((const char *)p_project_data.ptr(), p_project_data.size()); - str = str.replace("$additional_pbx_files", pbx_files); - str = str.replace("$additional_pbx_frameworks_build", pbx_frameworks_build); - str = str.replace("$additional_pbx_frameworks_refs", pbx_frameworks_refs); - str = str.replace("$additional_pbx_resources_build", pbx_resources_build); - str = str.replace("$additional_pbx_resources_refs", pbx_resources_refs); - str = str.replace("$pbx_embeded_frameworks", pbx_embeded_frameworks); - - CharString cs = str.utf8(); - p_project_data.resize(cs.size() - 1); - for (int i = 0; i < cs.size() - 1; i++) { - p_project_data.write[i] = cs[i]; - } -} - -Error EditorExportPlatformIOS::_copy_asset(const String &p_out_dir, const String &p_asset, const String *p_custom_file_name, bool p_is_framework, bool p_should_embed, Vector<IOSExportAsset> &r_exported_assets) { - DirAccess *filesystem_da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - ERR_FAIL_COND_V_MSG(!filesystem_da, ERR_CANT_CREATE, "Cannot create DirAccess for path '" + p_out_dir + "'."); - - String binary_name = p_out_dir.get_file().get_basename(); - - DirAccess *da = DirAccess::create_for_path(p_asset); - if (!da) { - memdelete(filesystem_da); - ERR_FAIL_V_MSG(ERR_CANT_CREATE, "Can't create directory: " + p_asset + "."); - } - bool file_exists = da->file_exists(p_asset); - bool dir_exists = da->dir_exists(p_asset); - if (!file_exists && !dir_exists) { - memdelete(da); - memdelete(filesystem_da); - return ERR_FILE_NOT_FOUND; - } - - String base_dir = p_asset.get_base_dir().replace("res://", ""); - String destination_dir; - String destination; - String asset_path; - - bool create_framework = false; - - if (p_is_framework && p_asset.ends_with(".dylib")) { - // For iOS we need to turn .dylib into .framework - // to be able to send application to AppStore - asset_path = String("dylibs").plus_file(base_dir); - - String file_name; - - if (!p_custom_file_name) { - file_name = p_asset.get_basename().get_file(); - } else { - file_name = *p_custom_file_name; - } - - String framework_name = file_name + ".framework"; - - asset_path = asset_path.plus_file(framework_name); - destination_dir = p_out_dir.plus_file(asset_path); - destination = destination_dir.plus_file(file_name); - create_framework = true; - } else if (p_is_framework && (p_asset.ends_with(".framework") || p_asset.ends_with(".xcframework"))) { - asset_path = String("dylibs").plus_file(base_dir); - - String file_name; - - if (!p_custom_file_name) { - file_name = p_asset.get_file(); - } else { - file_name = *p_custom_file_name; - } - - asset_path = asset_path.plus_file(file_name); - destination_dir = p_out_dir.plus_file(asset_path); - destination = destination_dir; - } else { - asset_path = base_dir; - - String file_name; - - if (!p_custom_file_name) { - file_name = p_asset.get_file(); - } else { - file_name = *p_custom_file_name; - } - - destination_dir = p_out_dir.plus_file(asset_path); - asset_path = asset_path.plus_file(file_name); - destination = p_out_dir.plus_file(asset_path); - } - - if (!filesystem_da->dir_exists(destination_dir)) { - Error make_dir_err = filesystem_da->make_dir_recursive(destination_dir); - if (make_dir_err) { - memdelete(da); - memdelete(filesystem_da); - return make_dir_err; - } - } - - Error err = dir_exists ? da->copy_dir(p_asset, destination) : da->copy(p_asset, destination); - memdelete(da); - if (err) { - memdelete(filesystem_da); - return err; - } - IOSExportAsset exported_asset = { binary_name.plus_file(asset_path), p_is_framework, p_should_embed }; - r_exported_assets.push_back(exported_asset); - - if (create_framework) { - String file_name; - - if (!p_custom_file_name) { - file_name = p_asset.get_basename().get_file(); - } else { - file_name = *p_custom_file_name; - } - - String framework_name = file_name + ".framework"; - - // Performing `install_name_tool -id @rpath/{name}.framework/{name} ./{name}` on dylib - { - List<String> install_name_args; - install_name_args.push_back("-id"); - install_name_args.push_back(String("@rpath").plus_file(framework_name).plus_file(file_name)); - install_name_args.push_back(destination); - - OS::get_singleton()->execute("install_name_tool", install_name_args); - } - - // Creating Info.plist - { - String info_plist_format = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" - "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" - "<plist version=\"1.0\">\n" - "<dict>\n" - "<key>CFBundleShortVersionString</key>\n" - "<string>1.0</string>\n" - "<key>CFBundleIdentifier</key>\n" - "<string>com.gdnative.framework.$name</string>\n" - "<key>CFBundleName</key>\n" - "<string>$name</string>\n" - "<key>CFBundleExecutable</key>\n" - "<string>$name</string>\n" - "<key>DTPlatformName</key>\n" - "<string>iphoneos</string>\n" - "<key>CFBundleInfoDictionaryVersion</key>\n" - "<string>6.0</string>\n" - "<key>CFBundleVersion</key>\n" - "<string>1</string>\n" - "<key>CFBundlePackageType</key>\n" - "<string>FMWK</string>\n" - "<key>MinimumOSVersion</key>\n" - "<string>10.0</string>\n" - "</dict>\n" - "</plist>"; - - String info_plist = info_plist_format.replace("$name", file_name); - - FileAccess *f = FileAccess::open(destination_dir.plus_file("Info.plist"), FileAccess::WRITE); - if (f) { - f->store_string(info_plist); - f->close(); - memdelete(f); - } - } - } - - memdelete(filesystem_da); - - return OK; -} - -Error EditorExportPlatformIOS::_export_additional_assets(const String &p_out_dir, const Vector<String> &p_assets, bool p_is_framework, bool p_should_embed, Vector<IOSExportAsset> &r_exported_assets) { - for (int f_idx = 0; f_idx < p_assets.size(); ++f_idx) { - String asset = p_assets[f_idx]; - if (!asset.begins_with("res://")) { - // either SDK-builtin or already a part of the export template - IOSExportAsset exported_asset = { asset, p_is_framework, p_should_embed }; - r_exported_assets.push_back(exported_asset); - } else { - Error err = _copy_asset(p_out_dir, asset, nullptr, p_is_framework, p_should_embed, r_exported_assets); - ERR_FAIL_COND_V(err, err); - } - } - - return OK; -} - -Error EditorExportPlatformIOS::_export_additional_assets(const String &p_out_dir, const Vector<SharedObject> &p_libraries, Vector<IOSExportAsset> &r_exported_assets) { - Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins(); - for (int i = 0; i < export_plugins.size(); i++) { - Vector<String> linked_frameworks = export_plugins[i]->get_ios_frameworks(); - Error err = _export_additional_assets(p_out_dir, linked_frameworks, true, false, r_exported_assets); - ERR_FAIL_COND_V(err, err); - - Vector<String> embedded_frameworks = export_plugins[i]->get_ios_embedded_frameworks(); - err = _export_additional_assets(p_out_dir, embedded_frameworks, true, true, r_exported_assets); - ERR_FAIL_COND_V(err, err); - - Vector<String> project_static_libs = export_plugins[i]->get_ios_project_static_libs(); - for (int j = 0; j < project_static_libs.size(); j++) { - project_static_libs.write[j] = project_static_libs[j].get_file(); // Only the file name as it's copied to the project - } - err = _export_additional_assets(p_out_dir, project_static_libs, true, true, r_exported_assets); - ERR_FAIL_COND_V(err, err); - - Vector<String> ios_bundle_files = export_plugins[i]->get_ios_bundle_files(); - err = _export_additional_assets(p_out_dir, ios_bundle_files, false, false, r_exported_assets); - ERR_FAIL_COND_V(err, err); - } - - Vector<String> library_paths; - for (int i = 0; i < p_libraries.size(); ++i) { - library_paths.push_back(p_libraries[i].path); - } - Error err = _export_additional_assets(p_out_dir, library_paths, true, true, r_exported_assets); - ERR_FAIL_COND_V(err, err); - - return OK; -} - -Vector<String> EditorExportPlatformIOS::_get_preset_architectures(const Ref<EditorExportPreset> &p_preset) { - Vector<ExportArchitecture> all_archs = _get_supported_architectures(); - Vector<String> enabled_archs; - for (int i = 0; i < all_archs.size(); ++i) { - bool is_enabled = p_preset->get("architectures/" + all_archs[i].name); - if (is_enabled) { - enabled_archs.push_back(all_archs[i].name); - } - } - return enabled_archs; -} - -Error EditorExportPlatformIOS::_export_ios_plugins(const Ref<EditorExportPreset> &p_preset, IOSConfigData &p_config_data, const String &dest_dir, Vector<IOSExportAsset> &r_exported_assets, bool p_debug) { - String plugin_definition_cpp_code; - String plugin_initialization_cpp_code; - String plugin_deinitialization_cpp_code; - - Vector<String> plugin_linked_dependencies; - Vector<String> plugin_embedded_dependencies; - Vector<String> plugin_files; - - Vector<PluginConfigIOS> enabled_plugins = get_enabled_plugins(p_preset); - - Vector<String> added_linked_dependenciy_names; - Vector<String> added_embedded_dependenciy_names; - HashMap<String, String> plist_values; - - Set<String> plugin_linker_flags; - - Error err; - - for (int i = 0; i < enabled_plugins.size(); i++) { - PluginConfigIOS plugin = enabled_plugins[i]; - - // Export plugin binary. - String plugin_main_binary = get_plugin_main_binary(plugin, p_debug); - String plugin_binary_result_file = plugin.binary.get_file(); - // We shouldn't embed .xcframework that contains static libraries. - // Static libraries are not embedded anyway. - err = _copy_asset(dest_dir, plugin_main_binary, &plugin_binary_result_file, true, false, r_exported_assets); - - ERR_FAIL_COND_V(err, err); - - // Adding dependencies. - // Use separate container for names to check for duplicates. - for (int j = 0; j < plugin.linked_dependencies.size(); j++) { - String dependency = plugin.linked_dependencies[j]; - String name = dependency.get_file(); - - if (added_linked_dependenciy_names.has(name)) { - continue; - } - - added_linked_dependenciy_names.push_back(name); - plugin_linked_dependencies.push_back(dependency); - } - - for (int j = 0; j < plugin.system_dependencies.size(); j++) { - String dependency = plugin.system_dependencies[j]; - String name = dependency.get_file(); - - if (added_linked_dependenciy_names.has(name)) { - continue; - } - - added_linked_dependenciy_names.push_back(name); - plugin_linked_dependencies.push_back(dependency); - } - - for (int j = 0; j < plugin.embedded_dependencies.size(); j++) { - String dependency = plugin.embedded_dependencies[j]; - String name = dependency.get_file(); - - if (added_embedded_dependenciy_names.has(name)) { - continue; - } - - added_embedded_dependenciy_names.push_back(name); - plugin_embedded_dependencies.push_back(dependency); - } - - plugin_files.append_array(plugin.files_to_copy); - - // Capabilities - // Also checking for duplicates. - for (int j = 0; j < plugin.capabilities.size(); j++) { - String capability = plugin.capabilities[j]; - - if (p_config_data.capabilities.has(capability)) { - continue; - } - - p_config_data.capabilities.push_back(capability); - } - - // Linker flags - // Checking duplicates - for (int j = 0; j < plugin.linker_flags.size(); j++) { - String linker_flag = plugin.linker_flags[j]; - plugin_linker_flags.insert(linker_flag); - } - - // Plist - // Using hash map container to remove duplicates - const String *K = nullptr; - - while ((K = plugin.plist.next(K))) { - String key = *K; - PluginConfigIOS::PlistItem item = plugin.plist[key]; - - String value; - - switch (item.type) { - case PluginConfigIOS::PlistItemType::STRING_INPUT: { - String preset_name = "plugins_plist/" + key; - String input_value = p_preset->get(preset_name); - value = "<string>" + input_value + "</string>"; - } break; - default: - value = item.value; - break; - } - - if (key.is_empty() || value.is_empty()) { - continue; - } - - String plist_key = "<key>" + key + "</key>"; - - plist_values[plist_key] = value; - } - - // CPP Code - String definition_comment = "// Plugin: " + plugin.name + "\n"; - String initialization_method = plugin.initialization_method + "();\n"; - String deinitialization_method = plugin.deinitialization_method + "();\n"; - - plugin_definition_cpp_code += definition_comment + - "extern void " + initialization_method + - "extern void " + deinitialization_method + "\n"; - - plugin_initialization_cpp_code += "\t" + initialization_method; - plugin_deinitialization_cpp_code += "\t" + deinitialization_method; - } - - // Updating `Info.plist` - { - const String *K = nullptr; - while ((K = plist_values.next(K))) { - String key = *K; - String value = plist_values[key]; - - if (key.is_empty() || value.is_empty()) { - continue; - } - - p_config_data.plist_content += key + value + "\n"; - } - } - - // Export files - { - // Export linked plugin dependency - err = _export_additional_assets(dest_dir, plugin_linked_dependencies, true, false, r_exported_assets); - ERR_FAIL_COND_V(err, err); - - // Export embedded plugin dependency - err = _export_additional_assets(dest_dir, plugin_embedded_dependencies, true, true, r_exported_assets); - ERR_FAIL_COND_V(err, err); - - // Export plugin files - err = _export_additional_assets(dest_dir, plugin_files, false, false, r_exported_assets); - ERR_FAIL_COND_V(err, err); - } - - // Update CPP - { - Dictionary plugin_format; - plugin_format["definition"] = plugin_definition_cpp_code; - plugin_format["initialization"] = plugin_initialization_cpp_code; - plugin_format["deinitialization"] = plugin_deinitialization_cpp_code; - - String plugin_cpp_code = "\n// Godot Plugins\n" - "void godot_ios_plugins_initialize();\n" - "void godot_ios_plugins_deinitialize();\n" - "// Exported Plugins\n\n" - "$definition" - "// Use Plugins\n" - "void godot_ios_plugins_initialize() {\n" - "$initialization" - "}\n\n" - "void godot_ios_plugins_deinitialize() {\n" - "$deinitialization" - "}\n"; - - p_config_data.cpp_code += plugin_cpp_code.format(plugin_format, "$_"); - } - - // Update Linker Flag Values - { - String result_linker_flags = " "; - for (Set<String>::Element *E = plugin_linker_flags.front(); E; E = E->next()) { - const String &flag = E->get(); - - if (flag.length() == 0) { - continue; - } - - if (result_linker_flags.length() > 0) { - result_linker_flags += ' '; - } - - result_linker_flags += flag; - } - result_linker_flags = result_linker_flags.replace("\"", "\\\""); - p_config_data.linker_flags += result_linker_flags; - } - - return OK; -} - -Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { - ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags); - - String src_pkg_name; - String dest_dir = p_path.get_base_dir() + "/"; - String binary_name = p_path.get_file().get_basename(); - - EditorProgress ep("export", "Exporting for iOS", 5, true); - - String team_id = p_preset->get("application/app_store_team_id"); - ERR_FAIL_COND_V_MSG(team_id.length() == 0, ERR_CANT_OPEN, "App Store Team ID not specified - cannot configure the project."); - - if (p_debug) { - src_pkg_name = p_preset->get("custom_template/debug"); - } else { - src_pkg_name = p_preset->get("custom_template/release"); - } - - if (src_pkg_name == "") { - String err; - src_pkg_name = find_export_template("iphone.zip", &err); - if (src_pkg_name == "") { - EditorNode::add_io_error(err); - return ERR_FILE_NOT_FOUND; - } - } - - if (!DirAccess::exists(dest_dir)) { - return ERR_FILE_BAD_PATH; - } - - DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - if (da) { - String current_dir = da->get_current_dir(); - - // remove leftovers from last export so they don't interfere - // in case some files are no longer needed - if (da->change_dir(dest_dir + binary_name + ".xcodeproj") == OK) { - da->erase_contents_recursive(); - } - if (da->change_dir(dest_dir + binary_name) == OK) { - da->erase_contents_recursive(); - } - - da->change_dir(current_dir); - - if (!da->dir_exists(dest_dir + binary_name)) { - Error err = da->make_dir(dest_dir + binary_name); - if (err) { - memdelete(da); - return err; - } - } - memdelete(da); - } - - if (ep.step("Making .pck", 0)) { - return ERR_SKIP; - } - String pack_path = dest_dir + binary_name + ".pck"; - Vector<SharedObject> libraries; - Error err = save_pack(p_preset, pack_path, &libraries); - if (err) { - return err; - } - - if (ep.step("Extracting and configuring Xcode project", 1)) { - return ERR_SKIP; - } - - String library_to_use = "libgodot.iphone." + String(p_debug ? "debug" : "release") + ".xcframework"; - - print_line("Static framework: " + library_to_use); - String pkg_name; - if (p_preset->get("application/name") != "") { - pkg_name = p_preset->get("application/name"); // app_name - } else if (String(ProjectSettings::get_singleton()->get("application/config/name")) != "") { - pkg_name = String(ProjectSettings::get_singleton()->get("application/config/name")); - } else { - pkg_name = "Unnamed"; - } - - bool found_library = false; - int total_size = 0; - - const String project_file = "godot_ios.xcodeproj/project.pbxproj"; - Set<String> files_to_parse; - files_to_parse.insert("godot_ios/godot_ios-Info.plist"); - files_to_parse.insert(project_file); - files_to_parse.insert("godot_ios/export_options.plist"); - files_to_parse.insert("godot_ios/dummy.cpp"); - files_to_parse.insert("godot_ios.xcodeproj/project.xcworkspace/contents.xcworkspacedata"); - files_to_parse.insert("godot_ios.xcodeproj/xcshareddata/xcschemes/godot_ios.xcscheme"); - files_to_parse.insert("godot_ios/godot_ios.entitlements"); - files_to_parse.insert("godot_ios/Launch Screen.storyboard"); - - IOSConfigData config_data = { - pkg_name, - binary_name, - _get_additional_plist_content(), - String(" ").join(_get_preset_architectures(p_preset)), - _get_linker_flags(), - _get_cpp_code(), - "", - "", - "", - "", - Vector<String>() - }; - - Vector<IOSExportAsset> assets; - - DirAccess *tmp_app_path = DirAccess::create_for_path(dest_dir); - ERR_FAIL_COND_V(!tmp_app_path, ERR_CANT_CREATE); - - print_line("Unzipping..."); - FileAccess *src_f = nullptr; - zlib_filefunc_def io = zipio_create_io_from_file(&src_f); - unzFile src_pkg_zip = unzOpen2(src_pkg_name.utf8().get_data(), &io); - if (!src_pkg_zip) { - EditorNode::add_io_error("Could not open export template (not a zip file?):\n" + src_pkg_name); - return ERR_CANT_OPEN; - } - - err = _export_ios_plugins(p_preset, config_data, dest_dir + binary_name, assets, p_debug); - ERR_FAIL_COND_V(err, err); - - //export rest of the files - int ret = unzGoToFirstFile(src_pkg_zip); - Vector<uint8_t> project_file_data; - while (ret == UNZ_OK) { -#if defined(OSX_ENABLED) || defined(X11_ENABLED) - bool is_execute = false; -#endif - - //get filename - unz_file_info info; - char fname[16384]; - ret = unzGetCurrentFileInfo(src_pkg_zip, &info, fname, 16384, nullptr, 0, nullptr, 0); - - String file = fname; - - print_line("READ: " + file); - Vector<uint8_t> data; - data.resize(info.uncompressed_size); - - //read - unzOpenCurrentFile(src_pkg_zip); - unzReadCurrentFile(src_pkg_zip, data.ptrw(), data.size()); - unzCloseCurrentFile(src_pkg_zip); - - //write - - file = file.replace_first("iphone/", ""); - - if (files_to_parse.has(file)) { - _fix_config_file(p_preset, data, config_data, p_debug); - } else if (file.begins_with("libgodot.iphone")) { - if (!file.begins_with(library_to_use) || file.ends_with(String("/empty"))) { - ret = unzGoToNextFile(src_pkg_zip); - continue; //ignore! - } - found_library = true; -#if defined(OSX_ENABLED) || defined(X11_ENABLED) - is_execute = true; -#endif - file = file.replace(library_to_use, binary_name + ".xcframework"); - } - - if (file == project_file) { - project_file_data = data; - } - - ///@TODO need to parse logo files - - if (data.size() > 0) { - file = file.replace("godot_ios", binary_name); - - print_line("ADDING: " + file + " size: " + itos(data.size())); - total_size += data.size(); - - /* write it into our folder structure */ - file = dest_dir + file; - - /* make sure this folder exists */ - String dir_name = file.get_base_dir(); - if (!tmp_app_path->dir_exists(dir_name)) { - print_line("Creating " + dir_name); - Error dir_err = tmp_app_path->make_dir_recursive(dir_name); - if (dir_err) { - ERR_PRINT("Can't create '" + dir_name + "'."); - unzClose(src_pkg_zip); - memdelete(tmp_app_path); - return ERR_CANT_CREATE; - } - } - - /* write the file */ - FileAccess *f = FileAccess::open(file, FileAccess::WRITE); - if (!f) { - ERR_PRINT("Can't write '" + file + "'."); - unzClose(src_pkg_zip); - memdelete(tmp_app_path); - return ERR_CANT_CREATE; - }; - f->store_buffer(data.ptr(), data.size()); - f->close(); - memdelete(f); - -#if defined(OSX_ENABLED) || defined(X11_ENABLED) - if (is_execute) { - // we need execute rights on this file - chmod(file.utf8().get_data(), 0755); - } -#endif - } - - ret = unzGoToNextFile(src_pkg_zip); - } - - /* we're done with our source zip */ - unzClose(src_pkg_zip); - - if (!found_library) { - ERR_PRINT("Requested template library '" + library_to_use + "' not found. It might be missing from your template archive."); - memdelete(tmp_app_path); - return ERR_FILE_NOT_FOUND; - } - - // Copy project static libs to the project - Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins(); - for (int i = 0; i < export_plugins.size(); i++) { - Vector<String> project_static_libs = export_plugins[i]->get_ios_project_static_libs(); - for (int j = 0; j < project_static_libs.size(); j++) { - const String &static_lib_path = project_static_libs[j]; - String dest_lib_file_path = dest_dir + static_lib_path.get_file(); - Error lib_copy_err = tmp_app_path->copy(static_lib_path, dest_lib_file_path); - if (lib_copy_err != OK) { - ERR_PRINT("Can't copy '" + static_lib_path + "'."); - memdelete(tmp_app_path); - return lib_copy_err; - } - } - } - - String iconset_dir = dest_dir + binary_name + "/Images.xcassets/AppIcon.appiconset/"; - err = OK; - if (!tmp_app_path->dir_exists(iconset_dir)) { - err = tmp_app_path->make_dir_recursive(iconset_dir); - } - memdelete(tmp_app_path); - if (err) { - return err; - } - - err = _export_icons(p_preset, iconset_dir); - if (err) { - return err; - } - - bool use_storyboard = p_preset->get("storyboard/use_launch_screen_storyboard"); - - String launch_image_path = dest_dir + binary_name + "/Images.xcassets/LaunchImage.launchimage/"; - String splash_image_path = dest_dir + binary_name + "/Images.xcassets/SplashImage.imageset/"; - - DirAccess *launch_screen_da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - - if (!launch_screen_da) { - return ERR_CANT_CREATE; - } - - if (use_storyboard) { - print_line("Using Launch Storyboard"); - - if (launch_screen_da->change_dir(launch_image_path) == OK) { - launch_screen_da->erase_contents_recursive(); - launch_screen_da->remove(launch_image_path); - } - - err = _export_loading_screen_file(p_preset, splash_image_path); - } else { - print_line("Using Launch Images"); - - const String launch_screen_path = dest_dir + binary_name + "/Launch Screen.storyboard"; - - launch_screen_da->remove(launch_screen_path); - - if (launch_screen_da->change_dir(splash_image_path) == OK) { - launch_screen_da->erase_contents_recursive(); - launch_screen_da->remove(splash_image_path); - } - - err = _export_loading_screen_images(p_preset, launch_image_path); - } - - memdelete(launch_screen_da); - - if (err) { - return err; - } - - print_line("Exporting additional assets"); - _export_additional_assets(dest_dir + binary_name, libraries, assets); - _add_assets_to_project(p_preset, project_file_data, assets); - String project_file_name = dest_dir + binary_name + ".xcodeproj/project.pbxproj"; - FileAccess *f = FileAccess::open(project_file_name, FileAccess::WRITE); - if (!f) { - ERR_PRINT("Can't write '" + project_file_name + "'."); - return ERR_CANT_CREATE; - }; - f->store_buffer(project_file_data.ptr(), project_file_data.size()); - f->close(); - memdelete(f); - -#ifdef OSX_ENABLED - if (ep.step("Code-signing dylibs", 2)) { - return ERR_SKIP; - } - DirAccess *dylibs_dir = DirAccess::open(dest_dir + binary_name + "/dylibs"); - ERR_FAIL_COND_V(!dylibs_dir, ERR_CANT_OPEN); - CodesignData codesign_data(p_preset, p_debug); - err = _walk_dir_recursive(dylibs_dir, _codesign, &codesign_data); - memdelete(dylibs_dir); - ERR_FAIL_COND_V(err, err); - - if (ep.step("Making .xcarchive", 3)) { - return ERR_SKIP; - } - String archive_path = p_path.get_basename() + ".xcarchive"; - List<String> archive_args; - archive_args.push_back("-project"); - archive_args.push_back(dest_dir + binary_name + ".xcodeproj"); - archive_args.push_back("-scheme"); - archive_args.push_back(binary_name); - archive_args.push_back("-sdk"); - archive_args.push_back("iphoneos"); - archive_args.push_back("-configuration"); - archive_args.push_back(p_debug ? "Debug" : "Release"); - archive_args.push_back("-destination"); - archive_args.push_back("generic/platform=iOS"); - archive_args.push_back("archive"); - archive_args.push_back("-archivePath"); - archive_args.push_back(archive_path); - err = OS::get_singleton()->execute("xcodebuild", archive_args); - ERR_FAIL_COND_V(err, err); - - if (ep.step("Making .ipa", 4)) { - return ERR_SKIP; - } - List<String> export_args; - export_args.push_back("-exportArchive"); - export_args.push_back("-archivePath"); - export_args.push_back(archive_path); - export_args.push_back("-exportOptionsPlist"); - export_args.push_back(dest_dir + binary_name + "/export_options.plist"); - export_args.push_back("-allowProvisioningUpdates"); - export_args.push_back("-exportPath"); - export_args.push_back(dest_dir); - err = OS::get_singleton()->execute("xcodebuild", export_args); - ERR_FAIL_COND_V(err, err); -#else - print_line(".ipa can only be built on macOS. Leaving Xcode project without building the package."); -#endif - - return OK; -} - -bool EditorExportPlatformIOS::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const { - String err; - bool valid = false; - - // Look for export templates (first official, and if defined custom templates). - - bool dvalid = exists_export_template("iphone.zip", &err); - bool rvalid = dvalid; // Both in the same ZIP. - - if (p_preset->get("custom_template/debug") != "") { - dvalid = FileAccess::exists(p_preset->get("custom_template/debug")); - if (!dvalid) { - err += TTR("Custom debug template not found.") + "\n"; - } - } - if (p_preset->get("custom_template/release") != "") { - rvalid = FileAccess::exists(p_preset->get("custom_template/release")); - if (!rvalid) { - err += TTR("Custom release template not found.") + "\n"; - } - } - - valid = dvalid || rvalid; - r_missing_templates = !valid; - - // Validate the rest of the configuration. - - String team_id = p_preset->get("application/app_store_team_id"); - if (team_id.length() == 0) { - err += TTR("App Store Team ID not specified - cannot configure the project.") + "\n"; - valid = false; - } - - String identifier = p_preset->get("application/bundle_identifier"); - String pn_err; - if (!is_package_name_valid(identifier, &pn_err)) { - err += TTR("Invalid Identifier:") + " " + pn_err + "\n"; - valid = false; - } - - for (uint64_t i = 0; i < (sizeof(icon_infos) / sizeof(icon_infos[0])); ++i) { - IconInfo info = icon_infos[i]; - String icon_path = p_preset->get(info.preset_key); - if (icon_path.length() == 0) { - if (info.is_required) { - err += TTR("Required icon is not specified in the preset.") + "\n"; - valid = false; - } - break; - } - } - - String etc_error = test_etc2_or_pvrtc(); - if (etc_error != String()) { - valid = false; - err += etc_error; - } - - if (!err.is_empty()) { - r_error = err; - } - - return valid; -} - -EditorExportPlatformIOS::EditorExportPlatformIOS() { - Ref<Image> img = memnew(Image(_iphone_logo)); - logo.instantiate(); - logo->create_from_image(img); - - plugins_changed.set(); - - check_for_changes_thread.start(_check_for_changes_poll_thread, this); -} - -EditorExportPlatformIOS::~EditorExportPlatformIOS() { - quit_request.set(); - check_for_changes_thread.wait_to_finish(); -} +#include "export_plugin.h" void register_iphone_exporter() { Ref<EditorExportPlatformIOS> platform; diff --git a/platform/iphone/export/export_plugin.cpp b/platform/iphone/export/export_plugin.cpp new file mode 100644 index 0000000000..69a8203e9f --- /dev/null +++ b/platform/iphone/export/export_plugin.cpp @@ -0,0 +1,1792 @@ +/*************************************************************************/ +/* export_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "export_plugin.h" + +void EditorExportPlatformIOS::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) { + String driver = ProjectSettings::get_singleton()->get("rendering/driver/driver_name"); + r_features->push_back("pvrtc"); + if (driver == "Vulkan") { + // FIXME: Review if this is correct. + r_features->push_back("etc2"); + } + + Vector<String> architectures = _get_preset_architectures(p_preset); + for (int i = 0; i < architectures.size(); ++i) { + r_features->push_back(architectures[i]); + } +} + +Vector<EditorExportPlatformIOS::ExportArchitecture> EditorExportPlatformIOS::_get_supported_architectures() { + Vector<ExportArchitecture> archs; + archs.push_back(ExportArchitecture("armv7", false)); // Disabled by default, not included in official templates. + archs.push_back(ExportArchitecture("arm64", true)); + return archs; +} + +struct LoadingScreenInfo { + const char *preset_key; + const char *export_name; + int width = 0; + int height = 0; + bool rotate = false; +}; + +static const LoadingScreenInfo loading_screen_infos[] = { + { "landscape_launch_screens/iphone_2436x1125", "Default-Landscape-X.png", 2436, 1125, false }, + { "landscape_launch_screens/iphone_2208x1242", "Default-Landscape-736h@3x.png", 2208, 1242, false }, + { "landscape_launch_screens/ipad_1024x768", "Default-Landscape.png", 1024, 768, false }, + { "landscape_launch_screens/ipad_2048x1536", "Default-Landscape@2x.png", 2048, 1536, false }, + + { "portrait_launch_screens/iphone_640x960", "Default-480h@2x.png", 640, 960, true }, + { "portrait_launch_screens/iphone_640x1136", "Default-568h@2x.png", 640, 1136, true }, + { "portrait_launch_screens/iphone_750x1334", "Default-667h@2x.png", 750, 1334, true }, + { "portrait_launch_screens/iphone_1125x2436", "Default-Portrait-X.png", 1125, 2436, true }, + { "portrait_launch_screens/ipad_768x1024", "Default-Portrait.png", 768, 1024, true }, + { "portrait_launch_screens/ipad_1536x2048", "Default-Portrait@2x.png", 1536, 2048, true }, + { "portrait_launch_screens/iphone_1242x2208", "Default-Portrait-736h@3x.png", 1242, 2208, true } +}; + +void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options) { + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), "")); + + Vector<ExportArchitecture> architectures = _get_supported_architectures(); + for (int i = 0; i < architectures.size(); ++i) { + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "architectures/" + architectures[i].name), architectures[i].is_default)); + } + + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/app_store_team_id"), "")); + + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/provisioning_profile_uuid_debug"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/code_sign_identity_debug", PROPERTY_HINT_PLACEHOLDER_TEXT, "iPhone Developer"), "iPhone Developer")); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/export_method_debug", PROPERTY_HINT_ENUM, "App Store,Development,Ad-Hoc,Enterprise"), 1)); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/provisioning_profile_uuid_release"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/code_sign_identity_release", PROPERTY_HINT_PLACEHOLDER_TEXT, "iPhone Distribution"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/export_method_release", PROPERTY_HINT_ENUM, "App Store,Development,Ad-Hoc,Enterprise"), 0)); + + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/targeted_device_family", PROPERTY_HINT_ENUM, "iPhone,iPad,iPhone & iPad"), 2)); + + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/info"), "Made with Godot Engine")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/bundle_identifier", PROPERTY_HINT_PLACEHOLDER_TEXT, "com.example.game"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/signature"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/short_version"), "1.0")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/version"), "1.0")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/copyright"), "")); + + Vector<PluginConfigIOS> found_plugins = get_plugins(); + for (int i = 0; i < found_plugins.size(); i++) { + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "plugins/" + found_plugins[i].name), false)); + } + + Set<String> plist_keys; + + for (int i = 0; i < found_plugins.size(); i++) { + // Editable plugin plist values + PluginConfigIOS plugin = found_plugins[i]; + const String *K = nullptr; + + while ((K = plugin.plist.next(K))) { + String key = *K; + PluginConfigIOS::PlistItem item = plugin.plist[key]; + switch (item.type) { + case PluginConfigIOS::PlistItemType::STRING_INPUT: { + String preset_name = "plugins_plist/" + key; + if (!plist_keys.has(preset_name)) { + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, preset_name), item.value)); + plist_keys.insert(preset_name); + } + } break; + default: + continue; + } + } + } + + plugins_changed.clear(); + plugins = found_plugins; + + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/access_wifi"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/push_notifications"), false)); + + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "user_data/accessible_from_files_app"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "user_data/accessible_from_itunes_sharing"), false)); + + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/camera_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the camera"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/microphone_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the microphone"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/photolibrary_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need access to the photo library"), "")); + + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "icons/generate_missing"), false)); + + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "required_icons/iphone_120x120", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPhone/iPod Touch with retina display + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "required_icons/ipad_76x76", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPad + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "required_icons/app_store_1024x1024", PROPERTY_HINT_FILE, "*.png"), "")); // App Store + + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/iphone_180x180", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPhone with retina HD display + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/ipad_152x152", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPad with retina display + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/ipad_167x167", PROPERTY_HINT_FILE, "*.png"), "")); // Home screen on iPad Pro + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/spotlight_40x40", PROPERTY_HINT_FILE, "*.png"), "")); // Spotlight + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/spotlight_80x80", PROPERTY_HINT_FILE, "*.png"), "")); // Spotlight on devices with retina display + + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "storyboard/use_launch_screen_storyboard"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "storyboard/image_scale_mode", PROPERTY_HINT_ENUM, "Same as Logo,Center,Scale to Fit,Scale to Fill,Scale"), 0)); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "storyboard/custom_image@2x", PROPERTY_HINT_FILE, "*.png"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "storyboard/custom_image@3x", PROPERTY_HINT_FILE, "*.png"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "storyboard/use_custom_bg_color"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::COLOR, "storyboard/custom_bg_color"), Color())); + + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "launch_screens/generate_missing"), false)); + + for (uint64_t i = 0; i < sizeof(loading_screen_infos) / sizeof(loading_screen_infos[0]); ++i) { + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, loading_screen_infos[i].preset_key, PROPERTY_HINT_FILE, "*.png"), "")); + } +} + +void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &pfile, const IOSConfigData &p_config, bool p_debug) { + static const String export_method_string[] = { + "app-store", + "development", + "ad-hoc", + "enterprise" + }; + static const String storyboard_image_scale_mode[] = { + "center", + "scaleAspectFit", + "scaleAspectFill", + "scaleToFill" + }; + String str; + String strnew; + str.parse_utf8((const char *)pfile.ptr(), pfile.size()); + Vector<String> lines = str.split("\n"); + for (int i = 0; i < lines.size(); i++) { + if (lines[i].find("$binary") != -1) { + strnew += lines[i].replace("$binary", p_config.binary_name) + "\n"; + } else if (lines[i].find("$modules_buildfile") != -1) { + strnew += lines[i].replace("$modules_buildfile", p_config.modules_buildfile) + "\n"; + } else if (lines[i].find("$modules_fileref") != -1) { + strnew += lines[i].replace("$modules_fileref", p_config.modules_fileref) + "\n"; + } else if (lines[i].find("$modules_buildphase") != -1) { + strnew += lines[i].replace("$modules_buildphase", p_config.modules_buildphase) + "\n"; + } else if (lines[i].find("$modules_buildgrp") != -1) { + strnew += lines[i].replace("$modules_buildgrp", p_config.modules_buildgrp) + "\n"; + } else if (lines[i].find("$name") != -1) { + strnew += lines[i].replace("$name", p_config.pkg_name) + "\n"; + } else if (lines[i].find("$info") != -1) { + strnew += lines[i].replace("$info", p_preset->get("application/info")) + "\n"; + } else if (lines[i].find("$bundle_identifier") != -1) { + strnew += lines[i].replace("$bundle_identifier", p_preset->get("application/bundle_identifier")) + "\n"; + } else if (lines[i].find("$short_version") != -1) { + strnew += lines[i].replace("$short_version", p_preset->get("application/short_version")) + "\n"; + } else if (lines[i].find("$version") != -1) { + strnew += lines[i].replace("$version", p_preset->get("application/version")) + "\n"; + } else if (lines[i].find("$signature") != -1) { + strnew += lines[i].replace("$signature", p_preset->get("application/signature")) + "\n"; + } else if (lines[i].find("$copyright") != -1) { + strnew += lines[i].replace("$copyright", p_preset->get("application/copyright")) + "\n"; + } else if (lines[i].find("$team_id") != -1) { + strnew += lines[i].replace("$team_id", p_preset->get("application/app_store_team_id")) + "\n"; + } else if (lines[i].find("$default_build_config") != -1) { + strnew += lines[i].replace("$default_build_config", p_debug ? "Debug" : "Release") + "\n"; + } else if (lines[i].find("$export_method") != -1) { + int export_method = p_preset->get(p_debug ? "application/export_method_debug" : "application/export_method_release"); + strnew += lines[i].replace("$export_method", export_method_string[export_method]) + "\n"; + } else if (lines[i].find("$provisioning_profile_uuid_release") != -1) { + strnew += lines[i].replace("$provisioning_profile_uuid_release", p_preset->get("application/provisioning_profile_uuid_release")) + "\n"; + } else if (lines[i].find("$provisioning_profile_uuid_debug") != -1) { + strnew += lines[i].replace("$provisioning_profile_uuid_debug", p_preset->get("application/provisioning_profile_uuid_debug")) + "\n"; + } else if (lines[i].find("$provisioning_profile_uuid") != -1) { + String uuid = p_debug ? p_preset->get("application/provisioning_profile_uuid_debug") : p_preset->get("application/provisioning_profile_uuid_release"); + strnew += lines[i].replace("$provisioning_profile_uuid", uuid) + "\n"; + } else if (lines[i].find("$code_sign_identity_debug") != -1) { + strnew += lines[i].replace("$code_sign_identity_debug", p_preset->get("application/code_sign_identity_debug")) + "\n"; + } else if (lines[i].find("$code_sign_identity_release") != -1) { + strnew += lines[i].replace("$code_sign_identity_release", p_preset->get("application/code_sign_identity_release")) + "\n"; + } else if (lines[i].find("$additional_plist_content") != -1) { + strnew += lines[i].replace("$additional_plist_content", p_config.plist_content) + "\n"; + } else if (lines[i].find("$godot_archs") != -1) { + strnew += lines[i].replace("$godot_archs", p_config.architectures) + "\n"; + } else if (lines[i].find("$linker_flags") != -1) { + strnew += lines[i].replace("$linker_flags", p_config.linker_flags) + "\n"; + } else if (lines[i].find("$targeted_device_family") != -1) { + String xcode_value; + switch ((int)p_preset->get("application/targeted_device_family")) { + case 0: // iPhone + xcode_value = "1"; + break; + case 1: // iPad + xcode_value = "2"; + break; + case 2: // iPhone & iPad + xcode_value = "1,2"; + break; + } + strnew += lines[i].replace("$targeted_device_family", xcode_value) + "\n"; + } else if (lines[i].find("$cpp_code") != -1) { + strnew += lines[i].replace("$cpp_code", p_config.cpp_code) + "\n"; + } else if (lines[i].find("$docs_in_place") != -1) { + strnew += lines[i].replace("$docs_in_place", ((bool)p_preset->get("user_data/accessible_from_files_app")) ? "<true/>" : "<false/>") + "\n"; + } else if (lines[i].find("$docs_sharing") != -1) { + strnew += lines[i].replace("$docs_sharing", ((bool)p_preset->get("user_data/accessible_from_itunes_sharing")) ? "<true/>" : "<false/>") + "\n"; + } else if (lines[i].find("$entitlements_push_notifications") != -1) { + bool is_on = p_preset->get("capabilities/push_notifications"); + strnew += lines[i].replace("$entitlements_push_notifications", is_on ? "<key>aps-environment</key><string>development</string>" : "") + "\n"; + } else if (lines[i].find("$required_device_capabilities") != -1) { + String capabilities; + + // I've removed armv7 as we can run on 64bit only devices + // Note that capabilities listed here are requirements for the app to be installed. + // They don't enable anything. + Vector<String> capabilities_list = p_config.capabilities; + + if ((bool)p_preset->get("capabilities/access_wifi") && !capabilities_list.has("wifi")) { + capabilities_list.push_back("wifi"); + } + + for (int idx = 0; idx < capabilities_list.size(); idx++) { + capabilities += "<string>" + capabilities_list[idx] + "</string>\n"; + } + + strnew += lines[i].replace("$required_device_capabilities", capabilities); + } else if (lines[i].find("$interface_orientations") != -1) { + String orientations; + const DisplayServer::ScreenOrientation screen_orientation = + DisplayServer::ScreenOrientation(int(GLOBAL_GET("display/window/handheld/orientation"))); + + switch (screen_orientation) { + case DisplayServer::SCREEN_LANDSCAPE: + orientations += "<string>UIInterfaceOrientationLandscapeLeft</string>\n"; + break; + case DisplayServer::SCREEN_PORTRAIT: + orientations += "<string>UIInterfaceOrientationPortrait</string>\n"; + break; + case DisplayServer::SCREEN_REVERSE_LANDSCAPE: + orientations += "<string>UIInterfaceOrientationLandscapeRight</string>\n"; + break; + case DisplayServer::SCREEN_REVERSE_PORTRAIT: + orientations += "<string>UIInterfaceOrientationPortraitUpsideDown</string>\n"; + break; + case DisplayServer::SCREEN_SENSOR_LANDSCAPE: + // Allow both landscape orientations depending on sensor direction. + orientations += "<string>UIInterfaceOrientationLandscapeLeft</string>\n"; + orientations += "<string>UIInterfaceOrientationLandscapeRight</string>\n"; + break; + case DisplayServer::SCREEN_SENSOR_PORTRAIT: + // Allow both portrait orientations depending on sensor direction. + orientations += "<string>UIInterfaceOrientationPortrait</string>\n"; + orientations += "<string>UIInterfaceOrientationPortraitUpsideDown</string>\n"; + break; + case DisplayServer::SCREEN_SENSOR: + // Allow all screen orientations depending on sensor direction. + orientations += "<string>UIInterfaceOrientationLandscapeLeft</string>\n"; + orientations += "<string>UIInterfaceOrientationLandscapeRight</string>\n"; + orientations += "<string>UIInterfaceOrientationPortrait</string>\n"; + orientations += "<string>UIInterfaceOrientationPortraitUpsideDown</string>\n"; + break; + } + + strnew += lines[i].replace("$interface_orientations", orientations); + } else if (lines[i].find("$camera_usage_description") != -1) { + String description = p_preset->get("privacy/camera_usage_description"); + strnew += lines[i].replace("$camera_usage_description", description) + "\n"; + } else if (lines[i].find("$microphone_usage_description") != -1) { + String description = p_preset->get("privacy/microphone_usage_description"); + strnew += lines[i].replace("$microphone_usage_description", description) + "\n"; + } else if (lines[i].find("$photolibrary_usage_description") != -1) { + String description = p_preset->get("privacy/photolibrary_usage_description"); + strnew += lines[i].replace("$photolibrary_usage_description", description) + "\n"; + } else if (lines[i].find("$plist_launch_screen_name") != -1) { + bool is_on = p_preset->get("storyboard/use_launch_screen_storyboard"); + String value = is_on ? "<key>UILaunchStoryboardName</key>\n<string>Launch Screen</string>" : ""; + strnew += lines[i].replace("$plist_launch_screen_name", value) + "\n"; + } else if (lines[i].find("$pbx_launch_screen_file_reference") != -1) { + bool is_on = p_preset->get("storyboard/use_launch_screen_storyboard"); + String value = is_on ? "90DD2D9D24B36E8000717FE1 = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = \"Launch Screen.storyboard\"; sourceTree = \"<group>\"; };" : ""; + strnew += lines[i].replace("$pbx_launch_screen_file_reference", value) + "\n"; + } else if (lines[i].find("$pbx_launch_screen_copy_files") != -1) { + bool is_on = p_preset->get("storyboard/use_launch_screen_storyboard"); + String value = is_on ? "90DD2D9D24B36E8000717FE1 /* Launch Screen.storyboard */," : ""; + strnew += lines[i].replace("$pbx_launch_screen_copy_files", value) + "\n"; + } else if (lines[i].find("$pbx_launch_screen_build_phase") != -1) { + bool is_on = p_preset->get("storyboard/use_launch_screen_storyboard"); + String value = is_on ? "90DD2D9E24B36E8000717FE1 /* Launch Screen.storyboard in Resources */," : ""; + strnew += lines[i].replace("$pbx_launch_screen_build_phase", value) + "\n"; + } else if (lines[i].find("$pbx_launch_screen_build_reference") != -1) { + bool is_on = p_preset->get("storyboard/use_launch_screen_storyboard"); + String value = is_on ? "90DD2D9E24B36E8000717FE1 /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 90DD2D9D24B36E8000717FE1 /* Launch Screen.storyboard */; };" : ""; + strnew += lines[i].replace("$pbx_launch_screen_build_reference", value) + "\n"; + } else if (lines[i].find("$pbx_launch_image_usage_setting") != -1) { + bool is_on = p_preset->get("storyboard/use_launch_screen_storyboard"); + String value = is_on ? "" : "ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;"; + strnew += lines[i].replace("$pbx_launch_image_usage_setting", value) + "\n"; + } else if (lines[i].find("$launch_screen_image_mode") != -1) { + int image_scale_mode = p_preset->get("storyboard/image_scale_mode"); + String value; + + switch (image_scale_mode) { + case 0: { + String logo_path = ProjectSettings::get_singleton()->get("application/boot_splash/image"); + bool is_on = ProjectSettings::get_singleton()->get("application/boot_splash/fullsize"); + // If custom logo is not specified, Godot does not scale default one, so we should do the same. + value = (is_on && logo_path.length() > 0) ? "scaleAspectFit" : "center"; + } break; + default: { + value = storyboard_image_scale_mode[image_scale_mode - 1]; + } + } + + strnew += lines[i].replace("$launch_screen_image_mode", value) + "\n"; + } else if (lines[i].find("$launch_screen_background_color") != -1) { + bool use_custom = p_preset->get("storyboard/use_custom_bg_color"); + Color color = use_custom ? p_preset->get("storyboard/custom_bg_color") : ProjectSettings::get_singleton()->get("application/boot_splash/bg_color"); + const String value_format = "red=\"$red\" green=\"$green\" blue=\"$blue\" alpha=\"$alpha\""; + + Dictionary value_dictionary; + value_dictionary["red"] = color.r; + value_dictionary["green"] = color.g; + value_dictionary["blue"] = color.b; + value_dictionary["alpha"] = color.a; + String value = value_format.format(value_dictionary, "$_"); + + strnew += lines[i].replace("$launch_screen_background_color", value) + "\n"; + } else { + strnew += lines[i] + "\n"; + } + } + + // !BAS! I'm assuming the 9 in the original code was a typo. I've added -1 or else it seems to also be adding our terminating zero... + // should apply the same fix in our OSX export. + CharString cs = strnew.utf8(); + pfile.resize(cs.size() - 1); + for (int i = 0; i < cs.size() - 1; i++) { + pfile.write[i] = cs[i]; + } +} + +String EditorExportPlatformIOS::_get_additional_plist_content() { + Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins(); + String result; + for (int i = 0; i < export_plugins.size(); ++i) { + result += export_plugins[i]->get_ios_plist_content(); + } + return result; +} + +String EditorExportPlatformIOS::_get_linker_flags() { + Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins(); + String result; + for (int i = 0; i < export_plugins.size(); ++i) { + String flags = export_plugins[i]->get_ios_linker_flags(); + if (flags.length() == 0) { + continue; + } + if (result.length() > 0) { + result += ' '; + } + result += flags; + } + // the flags will be enclosed in quotes, so need to escape them + return result.replace("\"", "\\\""); +} + +String EditorExportPlatformIOS::_get_cpp_code() { + Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins(); + String result; + for (int i = 0; i < export_plugins.size(); ++i) { + result += export_plugins[i]->get_ios_cpp_code(); + } + return result; +} + +void EditorExportPlatformIOS::_blend_and_rotate(Ref<Image> &p_dst, Ref<Image> &p_src, bool p_rot) { + ERR_FAIL_COND(p_dst.is_null()); + ERR_FAIL_COND(p_src.is_null()); + + int sw = p_rot ? p_src->get_height() : p_src->get_width(); + int sh = p_rot ? p_src->get_width() : p_src->get_height(); + + int x_pos = (p_dst->get_width() - sw) / 2; + int y_pos = (p_dst->get_height() - sh) / 2; + + int xs = (x_pos >= 0) ? 0 : -x_pos; + int ys = (y_pos >= 0) ? 0 : -y_pos; + + if (sw + x_pos > p_dst->get_width()) { + sw = p_dst->get_width() - x_pos; + } + if (sh + y_pos > p_dst->get_height()) { + sh = p_dst->get_height() - y_pos; + } + + for (int y = ys; y < sh; y++) { + for (int x = xs; x < sw; x++) { + Color sc = p_rot ? p_src->get_pixel(p_src->get_width() - y - 1, x) : p_src->get_pixel(x, y); + Color dc = p_dst->get_pixel(x_pos + x, y_pos + y); + dc.r = (double)(sc.a * sc.r + dc.a * (1.0 - sc.a) * dc.r); + dc.g = (double)(sc.a * sc.g + dc.a * (1.0 - sc.a) * dc.g); + dc.b = (double)(sc.a * sc.b + dc.a * (1.0 - sc.a) * dc.b); + dc.a = (double)(sc.a + dc.a * (1.0 - sc.a)); + p_dst->set_pixel(x_pos + x, y_pos + y, dc); + } + } +} + +struct IconInfo { + const char *preset_key; + const char *idiom; + const char *export_name; + const char *actual_size_side; + const char *scale; + const char *unscaled_size; + bool is_required = false; +}; + +static const IconInfo icon_infos[] = { + { "required_icons/iphone_120x120", "iphone", "Icon-120.png", "120", "2x", "60x60", true }, + { "required_icons/iphone_120x120", "iphone", "Icon-120.png", "120", "3x", "40x40", true }, + + { "required_icons/ipad_76x76", "ipad", "Icon-76.png", "76", "1x", "76x76", true }, + { "required_icons/app_store_1024x1024", "ios-marketing", "Icon-1024.png", "1024", "1x", "1024x1024", true }, + + { "optional_icons/iphone_180x180", "iphone", "Icon-180.png", "180", "3x", "60x60", false }, + + { "optional_icons/ipad_152x152", "ipad", "Icon-152.png", "152", "2x", "76x76", false }, + + { "optional_icons/ipad_167x167", "ipad", "Icon-167.png", "167", "2x", "83.5x83.5", false }, + + { "optional_icons/spotlight_40x40", "ipad", "Icon-40.png", "40", "1x", "40x40", false }, + + { "optional_icons/spotlight_80x80", "iphone", "Icon-80.png", "80", "2x", "40x40", false }, + { "optional_icons/spotlight_80x80", "ipad", "Icon-80.png", "80", "2x", "40x40", false } +}; + +Error EditorExportPlatformIOS::_export_icons(const Ref<EditorExportPreset> &p_preset, const String &p_iconset_dir) { + String json_description = "{\"images\":["; + String sizes; + + DirAccess *da = DirAccess::open(p_iconset_dir); + ERR_FAIL_COND_V_MSG(!da, ERR_CANT_OPEN, "Cannot open directory '" + p_iconset_dir + "'."); + + for (uint64_t i = 0; i < (sizeof(icon_infos) / sizeof(icon_infos[0])); ++i) { + IconInfo info = icon_infos[i]; + int side_size = String(info.actual_size_side).to_int(); + String icon_path = p_preset->get(info.preset_key); + if (icon_path.length() == 0) { + if ((bool)p_preset->get("icons/generate_missing")) { + // Resize main app icon + icon_path = ProjectSettings::get_singleton()->get("application/config/icon"); + Ref<Image> img = memnew(Image); + Error err = ImageLoader::load_image(icon_path, img); + if (err != OK) { + ERR_PRINT("Invalid icon (" + String(info.preset_key) + "): '" + icon_path + "'."); + return ERR_UNCONFIGURED; + } + img->resize(side_size, side_size); + err = img->save_png(p_iconset_dir + info.export_name); + if (err) { + String err_str = String("Failed to export icon(" + String(info.preset_key) + "): '" + icon_path + "'."); + ERR_PRINT(err_str.utf8().get_data()); + return err; + } + } else { + if (info.is_required) { + String err_str = String("Required icon (") + info.preset_key + ") is not specified in the preset."; + ERR_PRINT(err_str); + return ERR_UNCONFIGURED; + } else { + String err_str = String("Icon (") + info.preset_key + ") is not specified in the preset."; + WARN_PRINT(err_str); + } + continue; + } + } else { + // Load custom icon + Ref<Image> img = memnew(Image); + Error err = ImageLoader::load_image(icon_path, img); + if (err != OK) { + ERR_PRINT("Invalid icon (" + String(info.preset_key) + "): '" + icon_path + "'."); + return ERR_UNCONFIGURED; + } + if (img->get_width() != side_size || img->get_height() != side_size) { + ERR_PRINT("Invalid icon size (" + String(info.preset_key) + "): '" + icon_path + "'."); + return ERR_UNCONFIGURED; + } + + err = da->copy(icon_path, p_iconset_dir + info.export_name); + if (err) { + memdelete(da); + String err_str = String("Failed to export icon(" + String(info.preset_key) + "): '" + icon_path + "'."); + ERR_PRINT(err_str.utf8().get_data()); + return err; + } + } + sizes += String(info.actual_size_side) + "\n"; + if (i > 0) { + json_description += ","; + } + json_description += String("{"); + json_description += String("\"idiom\":") + "\"" + info.idiom + "\","; + json_description += String("\"size\":") + "\"" + info.unscaled_size + "\","; + json_description += String("\"scale\":") + "\"" + info.scale + "\","; + json_description += String("\"filename\":") + "\"" + info.export_name + "\""; + json_description += String("}"); + } + json_description += "]}"; + memdelete(da); + + FileAccess *json_file = FileAccess::open(p_iconset_dir + "Contents.json", FileAccess::WRITE); + ERR_FAIL_COND_V(!json_file, ERR_CANT_CREATE); + CharString json_utf8 = json_description.utf8(); + json_file->store_buffer((const uint8_t *)json_utf8.get_data(), json_utf8.length()); + memdelete(json_file); + + FileAccess *sizes_file = FileAccess::open(p_iconset_dir + "sizes", FileAccess::WRITE); + ERR_FAIL_COND_V(!sizes_file, ERR_CANT_CREATE); + CharString sizes_utf8 = sizes.utf8(); + sizes_file->store_buffer((const uint8_t *)sizes_utf8.get_data(), sizes_utf8.length()); + memdelete(sizes_file); + + return OK; +} + +Error EditorExportPlatformIOS::_export_loading_screen_file(const Ref<EditorExportPreset> &p_preset, const String &p_dest_dir) { + const String custom_launch_image_2x = p_preset->get("storyboard/custom_image@2x"); + const String custom_launch_image_3x = p_preset->get("storyboard/custom_image@3x"); + + if (custom_launch_image_2x.length() > 0 && custom_launch_image_3x.length() > 0) { + Ref<Image> image; + String image_path = p_dest_dir.plus_file("splash@2x.png"); + image.instantiate(); + Error err = image->load(custom_launch_image_2x); + + if (err) { + image.unref(); + return err; + } + + if (image->save_png(image_path) != OK) { + return ERR_FILE_CANT_WRITE; + } + + image.unref(); + image_path = p_dest_dir.plus_file("splash@3x.png"); + image.instantiate(); + err = image->load(custom_launch_image_3x); + + if (err) { + image.unref(); + return err; + } + + if (image->save_png(image_path) != OK) { + return ERR_FILE_CANT_WRITE; + } + } else { + Ref<Image> splash; + + const String splash_path = ProjectSettings::get_singleton()->get("application/boot_splash/image"); + + if (!splash_path.is_empty()) { + splash.instantiate(); + const Error err = splash->load(splash_path); + if (err) { + splash.unref(); + } + } + + if (splash.is_null()) { + splash = Ref<Image>(memnew(Image(boot_splash_png))); + } + + // Using same image for both @2x and @3x + // because Godot's own boot logo uses single image for all resolutions. + // Also not using @1x image, because devices using this image variant + // are not supported by iOS 9, which is minimal target. + const String splash_png_path_2x = p_dest_dir.plus_file("splash@2x.png"); + const String splash_png_path_3x = p_dest_dir.plus_file("splash@3x.png"); + + if (splash->save_png(splash_png_path_2x) != OK) { + return ERR_FILE_CANT_WRITE; + } + + if (splash->save_png(splash_png_path_3x) != OK) { + return ERR_FILE_CANT_WRITE; + } + } + + return OK; +} + +Error EditorExportPlatformIOS::_export_loading_screen_images(const Ref<EditorExportPreset> &p_preset, const String &p_dest_dir) { + DirAccess *da = DirAccess::open(p_dest_dir); + ERR_FAIL_COND_V_MSG(!da, ERR_CANT_OPEN, "Cannot open directory '" + p_dest_dir + "'."); + + for (uint64_t i = 0; i < sizeof(loading_screen_infos) / sizeof(loading_screen_infos[0]); ++i) { + LoadingScreenInfo info = loading_screen_infos[i]; + String loading_screen_file = p_preset->get(info.preset_key); + if (loading_screen_file.size() > 0) { + // Load custom loading screens + Ref<Image> img = memnew(Image); + Error err = ImageLoader::load_image(loading_screen_file, img); + if (err != OK) { + ERR_PRINT("Invalid loading screen (" + String(info.preset_key) + "): '" + loading_screen_file + "'."); + return ERR_UNCONFIGURED; + } + if (img->get_width() != info.width || img->get_height() != info.height) { + ERR_PRINT("Invalid loading screen size (" + String(info.preset_key) + "): '" + loading_screen_file + "'."); + return ERR_UNCONFIGURED; + } + err = da->copy(loading_screen_file, p_dest_dir + info.export_name); + if (err) { + memdelete(da); + String err_str = String("Failed to export loading screen (") + info.preset_key + ") from path '" + loading_screen_file + "'."; + ERR_PRINT(err_str.utf8().get_data()); + return err; + } + } else if ((bool)p_preset->get("launch_screens/generate_missing")) { + // Generate loading screen from the splash screen + Color boot_bg_color = ProjectSettings::get_singleton()->get("application/boot_splash/bg_color"); + String boot_logo_path = ProjectSettings::get_singleton()->get("application/boot_splash/image"); + bool boot_logo_scale = ProjectSettings::get_singleton()->get("application/boot_splash/fullsize"); + + Ref<Image> img = memnew(Image); + img->create(info.width, info.height, false, Image::FORMAT_RGBA8); + img->fill(boot_bg_color); + + Ref<Image> img_bs; + + if (boot_logo_path.length() > 0) { + img_bs = Ref<Image>(memnew(Image)); + ImageLoader::load_image(boot_logo_path, img_bs); + } + if (!img_bs.is_valid()) { + img_bs = Ref<Image>(memnew(Image(boot_splash_png))); + } + if (img_bs.is_valid()) { + float aspect_ratio = (float)img_bs->get_width() / (float)img_bs->get_height(); + if (info.rotate) { + if (boot_logo_scale) { + if (info.width * aspect_ratio <= info.height) { + img_bs->resize(info.width * aspect_ratio, info.width); + } else { + img_bs->resize(info.height, info.height / aspect_ratio); + } + } + } else { + if (boot_logo_scale) { + if (info.height * aspect_ratio <= info.width) { + img_bs->resize(info.height * aspect_ratio, info.height); + } else { + img_bs->resize(info.width, info.width / aspect_ratio); + } + } + } + _blend_and_rotate(img, img_bs, info.rotate); + } + Error err = img->save_png(p_dest_dir + info.export_name); + if (err) { + String err_str = String("Failed to export loading screen (") + info.preset_key + ") from splash screen."; + WARN_PRINT(err_str.utf8().get_data()); + } + } else { + String err_str = String("No loading screen (") + info.preset_key + ") specified."; + WARN_PRINT(err_str.utf8().get_data()); + } + } + memdelete(da); + + return OK; +} + +Error EditorExportPlatformIOS::_walk_dir_recursive(DirAccess *p_da, FileHandler p_handler, void *p_userdata) { + Vector<String> dirs; + String path; + String current_dir = p_da->get_current_dir(); + p_da->list_dir_begin(); + while ((path = p_da->get_next()).length() != 0) { + if (p_da->current_is_dir()) { + if (path != "." && path != "..") { + dirs.push_back(path); + } + } else { + Error err = p_handler(current_dir.plus_file(path), p_userdata); + if (err) { + p_da->list_dir_end(); + return err; + } + } + } + p_da->list_dir_end(); + + for (int i = 0; i < dirs.size(); ++i) { + String dir = dirs[i]; + p_da->change_dir(dir); + Error err = _walk_dir_recursive(p_da, p_handler, p_userdata); + p_da->change_dir(".."); + if (err) { + return err; + } + } + + return OK; +} + +struct CodesignData { + const Ref<EditorExportPreset> &preset; + bool debug = false; + + CodesignData(const Ref<EditorExportPreset> &p_preset, bool p_debug) : + preset(p_preset), + debug(p_debug) { + } +}; + +Error EditorExportPlatformIOS::_codesign(String p_file, void *p_userdata) { + if (p_file.ends_with(".dylib")) { + CodesignData *data = (CodesignData *)p_userdata; + print_line(String("Signing ") + p_file); + List<String> codesign_args; + codesign_args.push_back("-f"); + codesign_args.push_back("-s"); + codesign_args.push_back(data->preset->get(data->debug ? "application/code_sign_identity_debug" : "application/code_sign_identity_release")); + codesign_args.push_back(p_file); + return OS::get_singleton()->execute("codesign", codesign_args); + } + return OK; +} + +struct PbxId { +private: + static char _hex_char(uint8_t four_bits) { + if (four_bits < 10) { + return ('0' + four_bits); + } + return 'A' + (four_bits - 10); + } + + static String _hex_pad(uint32_t num) { + Vector<char> ret; + ret.resize(sizeof(num) * 2); + for (uint64_t i = 0; i < sizeof(num) * 2; ++i) { + uint8_t four_bits = (num >> (sizeof(num) * 8 - (i + 1) * 4)) & 0xF; + ret.write[i] = _hex_char(four_bits); + } + return String::utf8(ret.ptr(), ret.size()); + } + +public: + uint32_t high_bits; + uint32_t mid_bits; + uint32_t low_bits; + + String str() const { + return _hex_pad(high_bits) + _hex_pad(mid_bits) + _hex_pad(low_bits); + } + + PbxId &operator++() { + low_bits++; + if (!low_bits) { + mid_bits++; + if (!mid_bits) { + high_bits++; + } + } + + return *this; + } +}; + +struct ExportLibsData { + Vector<String> lib_paths; + String dest_dir; +}; + +void EditorExportPlatformIOS::_add_assets_to_project(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_project_data, const Vector<IOSExportAsset> &p_additional_assets) { + // that is just a random number, we just need Godot IDs not to clash with + // existing IDs in the project. + PbxId current_id = { 0x58938401, 0, 0 }; + String pbx_files; + String pbx_frameworks_build; + String pbx_frameworks_refs; + String pbx_resources_build; + String pbx_resources_refs; + String pbx_embeded_frameworks; + + const String file_info_format = String("$build_id = {isa = PBXBuildFile; fileRef = $ref_id; };\n") + + "$ref_id = {isa = PBXFileReference; lastKnownFileType = $file_type; name = \"$name\"; path = \"$file_path\"; sourceTree = \"<group>\"; };\n"; + + for (int i = 0; i < p_additional_assets.size(); ++i) { + String additional_asset_info_format = file_info_format; + + String build_id = (++current_id).str(); + String ref_id = (++current_id).str(); + String framework_id = ""; + + const IOSExportAsset &asset = p_additional_assets[i]; + + String type; + if (asset.exported_path.ends_with(".framework")) { + if (asset.should_embed) { + additional_asset_info_format += "$framework_id = {isa = PBXBuildFile; fileRef = $ref_id; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };\n"; + framework_id = (++current_id).str(); + pbx_embeded_frameworks += framework_id + ",\n"; + } + + type = "wrapper.framework"; + } else if (asset.exported_path.ends_with(".xcframework")) { + if (asset.should_embed) { + additional_asset_info_format += "$framework_id = {isa = PBXBuildFile; fileRef = $ref_id; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };\n"; + framework_id = (++current_id).str(); + pbx_embeded_frameworks += framework_id + ",\n"; + } + + type = "wrapper.xcframework"; + } else if (asset.exported_path.ends_with(".dylib")) { + type = "compiled.mach-o.dylib"; + } else if (asset.exported_path.ends_with(".a")) { + type = "archive.ar"; + } else { + type = "file"; + } + + String &pbx_build = asset.is_framework ? pbx_frameworks_build : pbx_resources_build; + String &pbx_refs = asset.is_framework ? pbx_frameworks_refs : pbx_resources_refs; + + if (pbx_build.length() > 0) { + pbx_build += ",\n"; + pbx_refs += ",\n"; + } + pbx_build += build_id; + pbx_refs += ref_id; + + Dictionary format_dict; + format_dict["build_id"] = build_id; + format_dict["ref_id"] = ref_id; + format_dict["name"] = asset.exported_path.get_file(); + format_dict["file_path"] = asset.exported_path; + format_dict["file_type"] = type; + if (framework_id.length() > 0) { + format_dict["framework_id"] = framework_id; + } + pbx_files += additional_asset_info_format.format(format_dict, "$_"); + } + + // Note, frameworks like gamekit are always included in our project.pbxprof file + // even if turned off in capabilities. + + String str = String::utf8((const char *)p_project_data.ptr(), p_project_data.size()); + str = str.replace("$additional_pbx_files", pbx_files); + str = str.replace("$additional_pbx_frameworks_build", pbx_frameworks_build); + str = str.replace("$additional_pbx_frameworks_refs", pbx_frameworks_refs); + str = str.replace("$additional_pbx_resources_build", pbx_resources_build); + str = str.replace("$additional_pbx_resources_refs", pbx_resources_refs); + str = str.replace("$pbx_embeded_frameworks", pbx_embeded_frameworks); + + CharString cs = str.utf8(); + p_project_data.resize(cs.size() - 1); + for (int i = 0; i < cs.size() - 1; i++) { + p_project_data.write[i] = cs[i]; + } +} + +Error EditorExportPlatformIOS::_copy_asset(const String &p_out_dir, const String &p_asset, const String *p_custom_file_name, bool p_is_framework, bool p_should_embed, Vector<IOSExportAsset> &r_exported_assets) { + DirAccess *filesystem_da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + ERR_FAIL_COND_V_MSG(!filesystem_da, ERR_CANT_CREATE, "Cannot create DirAccess for path '" + p_out_dir + "'."); + + String binary_name = p_out_dir.get_file().get_basename(); + + DirAccess *da = DirAccess::create_for_path(p_asset); + if (!da) { + memdelete(filesystem_da); + ERR_FAIL_V_MSG(ERR_CANT_CREATE, "Can't create directory: " + p_asset + "."); + } + bool file_exists = da->file_exists(p_asset); + bool dir_exists = da->dir_exists(p_asset); + if (!file_exists && !dir_exists) { + memdelete(da); + memdelete(filesystem_da); + return ERR_FILE_NOT_FOUND; + } + + String base_dir = p_asset.get_base_dir().replace("res://", ""); + String destination_dir; + String destination; + String asset_path; + + bool create_framework = false; + + if (p_is_framework && p_asset.ends_with(".dylib")) { + // For iOS we need to turn .dylib into .framework + // to be able to send application to AppStore + asset_path = String("dylibs").plus_file(base_dir); + + String file_name; + + if (!p_custom_file_name) { + file_name = p_asset.get_basename().get_file(); + } else { + file_name = *p_custom_file_name; + } + + String framework_name = file_name + ".framework"; + + asset_path = asset_path.plus_file(framework_name); + destination_dir = p_out_dir.plus_file(asset_path); + destination = destination_dir.plus_file(file_name); + create_framework = true; + } else if (p_is_framework && (p_asset.ends_with(".framework") || p_asset.ends_with(".xcframework"))) { + asset_path = String("dylibs").plus_file(base_dir); + + String file_name; + + if (!p_custom_file_name) { + file_name = p_asset.get_file(); + } else { + file_name = *p_custom_file_name; + } + + asset_path = asset_path.plus_file(file_name); + destination_dir = p_out_dir.plus_file(asset_path); + destination = destination_dir; + } else { + asset_path = base_dir; + + String file_name; + + if (!p_custom_file_name) { + file_name = p_asset.get_file(); + } else { + file_name = *p_custom_file_name; + } + + destination_dir = p_out_dir.plus_file(asset_path); + asset_path = asset_path.plus_file(file_name); + destination = p_out_dir.plus_file(asset_path); + } + + if (!filesystem_da->dir_exists(destination_dir)) { + Error make_dir_err = filesystem_da->make_dir_recursive(destination_dir); + if (make_dir_err) { + memdelete(da); + memdelete(filesystem_da); + return make_dir_err; + } + } + + Error err = dir_exists ? da->copy_dir(p_asset, destination) : da->copy(p_asset, destination); + memdelete(da); + if (err) { + memdelete(filesystem_da); + return err; + } + IOSExportAsset exported_asset = { binary_name.plus_file(asset_path), p_is_framework, p_should_embed }; + r_exported_assets.push_back(exported_asset); + + if (create_framework) { + String file_name; + + if (!p_custom_file_name) { + file_name = p_asset.get_basename().get_file(); + } else { + file_name = *p_custom_file_name; + } + + String framework_name = file_name + ".framework"; + + // Performing `install_name_tool -id @rpath/{name}.framework/{name} ./{name}` on dylib + { + List<String> install_name_args; + install_name_args.push_back("-id"); + install_name_args.push_back(String("@rpath").plus_file(framework_name).plus_file(file_name)); + install_name_args.push_back(destination); + + OS::get_singleton()->execute("install_name_tool", install_name_args); + } + + // Creating Info.plist + { + String info_plist_format = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" + "<plist version=\"1.0\">\n" + "<dict>\n" + "<key>CFBundleShortVersionString</key>\n" + "<string>1.0</string>\n" + "<key>CFBundleIdentifier</key>\n" + "<string>com.gdnative.framework.$name</string>\n" + "<key>CFBundleName</key>\n" + "<string>$name</string>\n" + "<key>CFBundleExecutable</key>\n" + "<string>$name</string>\n" + "<key>DTPlatformName</key>\n" + "<string>iphoneos</string>\n" + "<key>CFBundleInfoDictionaryVersion</key>\n" + "<string>6.0</string>\n" + "<key>CFBundleVersion</key>\n" + "<string>1</string>\n" + "<key>CFBundlePackageType</key>\n" + "<string>FMWK</string>\n" + "<key>MinimumOSVersion</key>\n" + "<string>10.0</string>\n" + "</dict>\n" + "</plist>"; + + String info_plist = info_plist_format.replace("$name", file_name); + + FileAccess *f = FileAccess::open(destination_dir.plus_file("Info.plist"), FileAccess::WRITE); + if (f) { + f->store_string(info_plist); + f->close(); + memdelete(f); + } + } + } + + memdelete(filesystem_da); + + return OK; +} + +Error EditorExportPlatformIOS::_export_additional_assets(const String &p_out_dir, const Vector<String> &p_assets, bool p_is_framework, bool p_should_embed, Vector<IOSExportAsset> &r_exported_assets) { + for (int f_idx = 0; f_idx < p_assets.size(); ++f_idx) { + String asset = p_assets[f_idx]; + if (!asset.begins_with("res://")) { + // either SDK-builtin or already a part of the export template + IOSExportAsset exported_asset = { asset, p_is_framework, p_should_embed }; + r_exported_assets.push_back(exported_asset); + } else { + Error err = _copy_asset(p_out_dir, asset, nullptr, p_is_framework, p_should_embed, r_exported_assets); + ERR_FAIL_COND_V(err, err); + } + } + + return OK; +} + +Error EditorExportPlatformIOS::_export_additional_assets(const String &p_out_dir, const Vector<SharedObject> &p_libraries, Vector<IOSExportAsset> &r_exported_assets) { + Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins(); + for (int i = 0; i < export_plugins.size(); i++) { + Vector<String> linked_frameworks = export_plugins[i]->get_ios_frameworks(); + Error err = _export_additional_assets(p_out_dir, linked_frameworks, true, false, r_exported_assets); + ERR_FAIL_COND_V(err, err); + + Vector<String> embedded_frameworks = export_plugins[i]->get_ios_embedded_frameworks(); + err = _export_additional_assets(p_out_dir, embedded_frameworks, true, true, r_exported_assets); + ERR_FAIL_COND_V(err, err); + + Vector<String> project_static_libs = export_plugins[i]->get_ios_project_static_libs(); + for (int j = 0; j < project_static_libs.size(); j++) { + project_static_libs.write[j] = project_static_libs[j].get_file(); // Only the file name as it's copied to the project + } + err = _export_additional_assets(p_out_dir, project_static_libs, true, true, r_exported_assets); + ERR_FAIL_COND_V(err, err); + + Vector<String> ios_bundle_files = export_plugins[i]->get_ios_bundle_files(); + err = _export_additional_assets(p_out_dir, ios_bundle_files, false, false, r_exported_assets); + ERR_FAIL_COND_V(err, err); + } + + Vector<String> library_paths; + for (int i = 0; i < p_libraries.size(); ++i) { + library_paths.push_back(p_libraries[i].path); + } + Error err = _export_additional_assets(p_out_dir, library_paths, true, true, r_exported_assets); + ERR_FAIL_COND_V(err, err); + + return OK; +} + +Vector<String> EditorExportPlatformIOS::_get_preset_architectures(const Ref<EditorExportPreset> &p_preset) { + Vector<ExportArchitecture> all_archs = _get_supported_architectures(); + Vector<String> enabled_archs; + for (int i = 0; i < all_archs.size(); ++i) { + bool is_enabled = p_preset->get("architectures/" + all_archs[i].name); + if (is_enabled) { + enabled_archs.push_back(all_archs[i].name); + } + } + return enabled_archs; +} + +Error EditorExportPlatformIOS::_export_ios_plugins(const Ref<EditorExportPreset> &p_preset, IOSConfigData &p_config_data, const String &dest_dir, Vector<IOSExportAsset> &r_exported_assets, bool p_debug) { + String plugin_definition_cpp_code; + String plugin_initialization_cpp_code; + String plugin_deinitialization_cpp_code; + + Vector<String> plugin_linked_dependencies; + Vector<String> plugin_embedded_dependencies; + Vector<String> plugin_files; + + Vector<PluginConfigIOS> enabled_plugins = get_enabled_plugins(p_preset); + + Vector<String> added_linked_dependenciy_names; + Vector<String> added_embedded_dependenciy_names; + HashMap<String, String> plist_values; + + Set<String> plugin_linker_flags; + + Error err; + + for (int i = 0; i < enabled_plugins.size(); i++) { + PluginConfigIOS plugin = enabled_plugins[i]; + + // Export plugin binary. + String plugin_main_binary = PluginConfigIOS::get_plugin_main_binary(plugin, p_debug); + String plugin_binary_result_file = plugin.binary.get_file(); + // We shouldn't embed .xcframework that contains static libraries. + // Static libraries are not embedded anyway. + err = _copy_asset(dest_dir, plugin_main_binary, &plugin_binary_result_file, true, false, r_exported_assets); + + ERR_FAIL_COND_V(err, err); + + // Adding dependencies. + // Use separate container for names to check for duplicates. + for (int j = 0; j < plugin.linked_dependencies.size(); j++) { + String dependency = plugin.linked_dependencies[j]; + String name = dependency.get_file(); + + if (added_linked_dependenciy_names.has(name)) { + continue; + } + + added_linked_dependenciy_names.push_back(name); + plugin_linked_dependencies.push_back(dependency); + } + + for (int j = 0; j < plugin.system_dependencies.size(); j++) { + String dependency = plugin.system_dependencies[j]; + String name = dependency.get_file(); + + if (added_linked_dependenciy_names.has(name)) { + continue; + } + + added_linked_dependenciy_names.push_back(name); + plugin_linked_dependencies.push_back(dependency); + } + + for (int j = 0; j < plugin.embedded_dependencies.size(); j++) { + String dependency = plugin.embedded_dependencies[j]; + String name = dependency.get_file(); + + if (added_embedded_dependenciy_names.has(name)) { + continue; + } + + added_embedded_dependenciy_names.push_back(name); + plugin_embedded_dependencies.push_back(dependency); + } + + plugin_files.append_array(plugin.files_to_copy); + + // Capabilities + // Also checking for duplicates. + for (int j = 0; j < plugin.capabilities.size(); j++) { + String capability = plugin.capabilities[j]; + + if (p_config_data.capabilities.has(capability)) { + continue; + } + + p_config_data.capabilities.push_back(capability); + } + + // Linker flags + // Checking duplicates + for (int j = 0; j < plugin.linker_flags.size(); j++) { + String linker_flag = plugin.linker_flags[j]; + plugin_linker_flags.insert(linker_flag); + } + + // Plist + // Using hash map container to remove duplicates + const String *K = nullptr; + + while ((K = plugin.plist.next(K))) { + String key = *K; + PluginConfigIOS::PlistItem item = plugin.plist[key]; + + String value; + + switch (item.type) { + case PluginConfigIOS::PlistItemType::STRING_INPUT: { + String preset_name = "plugins_plist/" + key; + String input_value = p_preset->get(preset_name); + value = "<string>" + input_value + "</string>"; + } break; + default: + value = item.value; + break; + } + + if (key.is_empty() || value.is_empty()) { + continue; + } + + String plist_key = "<key>" + key + "</key>"; + + plist_values[plist_key] = value; + } + + // CPP Code + String definition_comment = "// Plugin: " + plugin.name + "\n"; + String initialization_method = plugin.initialization_method + "();\n"; + String deinitialization_method = plugin.deinitialization_method + "();\n"; + + plugin_definition_cpp_code += definition_comment + + "extern void " + initialization_method + + "extern void " + deinitialization_method + "\n"; + + plugin_initialization_cpp_code += "\t" + initialization_method; + plugin_deinitialization_cpp_code += "\t" + deinitialization_method; + } + + // Updating `Info.plist` + { + const String *K = nullptr; + while ((K = plist_values.next(K))) { + String key = *K; + String value = plist_values[key]; + + if (key.is_empty() || value.is_empty()) { + continue; + } + + p_config_data.plist_content += key + value + "\n"; + } + } + + // Export files + { + // Export linked plugin dependency + err = _export_additional_assets(dest_dir, plugin_linked_dependencies, true, false, r_exported_assets); + ERR_FAIL_COND_V(err, err); + + // Export embedded plugin dependency + err = _export_additional_assets(dest_dir, plugin_embedded_dependencies, true, true, r_exported_assets); + ERR_FAIL_COND_V(err, err); + + // Export plugin files + err = _export_additional_assets(dest_dir, plugin_files, false, false, r_exported_assets); + ERR_FAIL_COND_V(err, err); + } + + // Update CPP + { + Dictionary plugin_format; + plugin_format["definition"] = plugin_definition_cpp_code; + plugin_format["initialization"] = plugin_initialization_cpp_code; + plugin_format["deinitialization"] = plugin_deinitialization_cpp_code; + + String plugin_cpp_code = "\n// Godot Plugins\n" + "void godot_ios_plugins_initialize();\n" + "void godot_ios_plugins_deinitialize();\n" + "// Exported Plugins\n\n" + "$definition" + "// Use Plugins\n" + "void godot_ios_plugins_initialize() {\n" + "$initialization" + "}\n\n" + "void godot_ios_plugins_deinitialize() {\n" + "$deinitialization" + "}\n"; + + p_config_data.cpp_code += plugin_cpp_code.format(plugin_format, "$_"); + } + + // Update Linker Flag Values + { + String result_linker_flags = " "; + for (Set<String>::Element *E = plugin_linker_flags.front(); E; E = E->next()) { + const String &flag = E->get(); + + if (flag.length() == 0) { + continue; + } + + if (result_linker_flags.length() > 0) { + result_linker_flags += ' '; + } + + result_linker_flags += flag; + } + result_linker_flags = result_linker_flags.replace("\"", "\\\""); + p_config_data.linker_flags += result_linker_flags; + } + + return OK; +} + +Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { + ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags); + + String src_pkg_name; + String dest_dir = p_path.get_base_dir() + "/"; + String binary_name = p_path.get_file().get_basename(); + + EditorProgress ep("export", "Exporting for iOS", 5, true); + + String team_id = p_preset->get("application/app_store_team_id"); + ERR_FAIL_COND_V_MSG(team_id.length() == 0, ERR_CANT_OPEN, "App Store Team ID not specified - cannot configure the project."); + + if (p_debug) { + src_pkg_name = p_preset->get("custom_template/debug"); + } else { + src_pkg_name = p_preset->get("custom_template/release"); + } + + if (src_pkg_name == "") { + String err; + src_pkg_name = find_export_template("iphone.zip", &err); + if (src_pkg_name == "") { + EditorNode::add_io_error(err); + return ERR_FILE_NOT_FOUND; + } + } + + if (!DirAccess::exists(dest_dir)) { + return ERR_FILE_BAD_PATH; + } + + DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + if (da) { + String current_dir = da->get_current_dir(); + + // remove leftovers from last export so they don't interfere + // in case some files are no longer needed + if (da->change_dir(dest_dir + binary_name + ".xcodeproj") == OK) { + da->erase_contents_recursive(); + } + if (da->change_dir(dest_dir + binary_name) == OK) { + da->erase_contents_recursive(); + } + + da->change_dir(current_dir); + + if (!da->dir_exists(dest_dir + binary_name)) { + Error err = da->make_dir(dest_dir + binary_name); + if (err) { + memdelete(da); + return err; + } + } + memdelete(da); + } + + if (ep.step("Making .pck", 0)) { + return ERR_SKIP; + } + String pack_path = dest_dir + binary_name + ".pck"; + Vector<SharedObject> libraries; + Error err = save_pack(p_preset, pack_path, &libraries); + if (err) { + return err; + } + + if (ep.step("Extracting and configuring Xcode project", 1)) { + return ERR_SKIP; + } + + String library_to_use = "libgodot.iphone." + String(p_debug ? "debug" : "release") + ".xcframework"; + + print_line("Static framework: " + library_to_use); + String pkg_name; + if (p_preset->get("application/name") != "") { + pkg_name = p_preset->get("application/name"); // app_name + } else if (String(ProjectSettings::get_singleton()->get("application/config/name")) != "") { + pkg_name = String(ProjectSettings::get_singleton()->get("application/config/name")); + } else { + pkg_name = "Unnamed"; + } + + bool found_library = false; + int total_size = 0; + + const String project_file = "godot_ios.xcodeproj/project.pbxproj"; + Set<String> files_to_parse; + files_to_parse.insert("godot_ios/godot_ios-Info.plist"); + files_to_parse.insert(project_file); + files_to_parse.insert("godot_ios/export_options.plist"); + files_to_parse.insert("godot_ios/dummy.cpp"); + files_to_parse.insert("godot_ios.xcodeproj/project.xcworkspace/contents.xcworkspacedata"); + files_to_parse.insert("godot_ios.xcodeproj/xcshareddata/xcschemes/godot_ios.xcscheme"); + files_to_parse.insert("godot_ios/godot_ios.entitlements"); + files_to_parse.insert("godot_ios/Launch Screen.storyboard"); + + IOSConfigData config_data = { + pkg_name, + binary_name, + _get_additional_plist_content(), + String(" ").join(_get_preset_architectures(p_preset)), + _get_linker_flags(), + _get_cpp_code(), + "", + "", + "", + "", + Vector<String>() + }; + + Vector<IOSExportAsset> assets; + + DirAccess *tmp_app_path = DirAccess::create_for_path(dest_dir); + ERR_FAIL_COND_V(!tmp_app_path, ERR_CANT_CREATE); + + print_line("Unzipping..."); + FileAccess *src_f = nullptr; + zlib_filefunc_def io = zipio_create_io_from_file(&src_f); + unzFile src_pkg_zip = unzOpen2(src_pkg_name.utf8().get_data(), &io); + if (!src_pkg_zip) { + EditorNode::add_io_error("Could not open export template (not a zip file?):\n" + src_pkg_name); + return ERR_CANT_OPEN; + } + + err = _export_ios_plugins(p_preset, config_data, dest_dir + binary_name, assets, p_debug); + ERR_FAIL_COND_V(err, err); + + //export rest of the files + int ret = unzGoToFirstFile(src_pkg_zip); + Vector<uint8_t> project_file_data; + while (ret == UNZ_OK) { +#if defined(OSX_ENABLED) || defined(X11_ENABLED) + bool is_execute = false; +#endif + + //get filename + unz_file_info info; + char fname[16384]; + ret = unzGetCurrentFileInfo(src_pkg_zip, &info, fname, 16384, nullptr, 0, nullptr, 0); + + String file = fname; + + print_line("READ: " + file); + Vector<uint8_t> data; + data.resize(info.uncompressed_size); + + //read + unzOpenCurrentFile(src_pkg_zip); + unzReadCurrentFile(src_pkg_zip, data.ptrw(), data.size()); + unzCloseCurrentFile(src_pkg_zip); + + //write + + file = file.replace_first("iphone/", ""); + + if (files_to_parse.has(file)) { + _fix_config_file(p_preset, data, config_data, p_debug); + } else if (file.begins_with("libgodot.iphone")) { + if (!file.begins_with(library_to_use) || file.ends_with(String("/empty"))) { + ret = unzGoToNextFile(src_pkg_zip); + continue; //ignore! + } + found_library = true; +#if defined(OSX_ENABLED) || defined(X11_ENABLED) + is_execute = true; +#endif + file = file.replace(library_to_use, binary_name + ".xcframework"); + } + + if (file == project_file) { + project_file_data = data; + } + + ///@TODO need to parse logo files + + if (data.size() > 0) { + file = file.replace("godot_ios", binary_name); + + print_line("ADDING: " + file + " size: " + itos(data.size())); + total_size += data.size(); + + /* write it into our folder structure */ + file = dest_dir + file; + + /* make sure this folder exists */ + String dir_name = file.get_base_dir(); + if (!tmp_app_path->dir_exists(dir_name)) { + print_line("Creating " + dir_name); + Error dir_err = tmp_app_path->make_dir_recursive(dir_name); + if (dir_err) { + ERR_PRINT("Can't create '" + dir_name + "'."); + unzClose(src_pkg_zip); + memdelete(tmp_app_path); + return ERR_CANT_CREATE; + } + } + + /* write the file */ + FileAccess *f = FileAccess::open(file, FileAccess::WRITE); + if (!f) { + ERR_PRINT("Can't write '" + file + "'."); + unzClose(src_pkg_zip); + memdelete(tmp_app_path); + return ERR_CANT_CREATE; + }; + f->store_buffer(data.ptr(), data.size()); + f->close(); + memdelete(f); + +#if defined(OSX_ENABLED) || defined(X11_ENABLED) + if (is_execute) { + // we need execute rights on this file + chmod(file.utf8().get_data(), 0755); + } +#endif + } + + ret = unzGoToNextFile(src_pkg_zip); + } + + /* we're done with our source zip */ + unzClose(src_pkg_zip); + + if (!found_library) { + ERR_PRINT("Requested template library '" + library_to_use + "' not found. It might be missing from your template archive."); + memdelete(tmp_app_path); + return ERR_FILE_NOT_FOUND; + } + + // Copy project static libs to the project + Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins(); + for (int i = 0; i < export_plugins.size(); i++) { + Vector<String> project_static_libs = export_plugins[i]->get_ios_project_static_libs(); + for (int j = 0; j < project_static_libs.size(); j++) { + const String &static_lib_path = project_static_libs[j]; + String dest_lib_file_path = dest_dir + static_lib_path.get_file(); + Error lib_copy_err = tmp_app_path->copy(static_lib_path, dest_lib_file_path); + if (lib_copy_err != OK) { + ERR_PRINT("Can't copy '" + static_lib_path + "'."); + memdelete(tmp_app_path); + return lib_copy_err; + } + } + } + + String iconset_dir = dest_dir + binary_name + "/Images.xcassets/AppIcon.appiconset/"; + err = OK; + if (!tmp_app_path->dir_exists(iconset_dir)) { + err = tmp_app_path->make_dir_recursive(iconset_dir); + } + memdelete(tmp_app_path); + if (err) { + return err; + } + + err = _export_icons(p_preset, iconset_dir); + if (err) { + return err; + } + + bool use_storyboard = p_preset->get("storyboard/use_launch_screen_storyboard"); + + String launch_image_path = dest_dir + binary_name + "/Images.xcassets/LaunchImage.launchimage/"; + String splash_image_path = dest_dir + binary_name + "/Images.xcassets/SplashImage.imageset/"; + + DirAccess *launch_screen_da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + + if (!launch_screen_da) { + return ERR_CANT_CREATE; + } + + if (use_storyboard) { + print_line("Using Launch Storyboard"); + + if (launch_screen_da->change_dir(launch_image_path) == OK) { + launch_screen_da->erase_contents_recursive(); + launch_screen_da->remove(launch_image_path); + } + + err = _export_loading_screen_file(p_preset, splash_image_path); + } else { + print_line("Using Launch Images"); + + const String launch_screen_path = dest_dir + binary_name + "/Launch Screen.storyboard"; + + launch_screen_da->remove(launch_screen_path); + + if (launch_screen_da->change_dir(splash_image_path) == OK) { + launch_screen_da->erase_contents_recursive(); + launch_screen_da->remove(splash_image_path); + } + + err = _export_loading_screen_images(p_preset, launch_image_path); + } + + memdelete(launch_screen_da); + + if (err) { + return err; + } + + print_line("Exporting additional assets"); + _export_additional_assets(dest_dir + binary_name, libraries, assets); + _add_assets_to_project(p_preset, project_file_data, assets); + String project_file_name = dest_dir + binary_name + ".xcodeproj/project.pbxproj"; + FileAccess *f = FileAccess::open(project_file_name, FileAccess::WRITE); + if (!f) { + ERR_PRINT("Can't write '" + project_file_name + "'."); + return ERR_CANT_CREATE; + }; + f->store_buffer(project_file_data.ptr(), project_file_data.size()); + f->close(); + memdelete(f); + +#ifdef OSX_ENABLED + if (ep.step("Code-signing dylibs", 2)) { + return ERR_SKIP; + } + DirAccess *dylibs_dir = DirAccess::open(dest_dir + binary_name + "/dylibs"); + ERR_FAIL_COND_V(!dylibs_dir, ERR_CANT_OPEN); + CodesignData codesign_data(p_preset, p_debug); + err = _walk_dir_recursive(dylibs_dir, _codesign, &codesign_data); + memdelete(dylibs_dir); + ERR_FAIL_COND_V(err, err); + + if (ep.step("Making .xcarchive", 3)) { + return ERR_SKIP; + } + String archive_path = p_path.get_basename() + ".xcarchive"; + List<String> archive_args; + archive_args.push_back("-project"); + archive_args.push_back(dest_dir + binary_name + ".xcodeproj"); + archive_args.push_back("-scheme"); + archive_args.push_back(binary_name); + archive_args.push_back("-sdk"); + archive_args.push_back("iphoneos"); + archive_args.push_back("-configuration"); + archive_args.push_back(p_debug ? "Debug" : "Release"); + archive_args.push_back("-destination"); + archive_args.push_back("generic/platform=iOS"); + archive_args.push_back("archive"); + archive_args.push_back("-archivePath"); + archive_args.push_back(archive_path); + err = OS::get_singleton()->execute("xcodebuild", archive_args); + ERR_FAIL_COND_V(err, err); + + if (ep.step("Making .ipa", 4)) { + return ERR_SKIP; + } + List<String> export_args; + export_args.push_back("-exportArchive"); + export_args.push_back("-archivePath"); + export_args.push_back(archive_path); + export_args.push_back("-exportOptionsPlist"); + export_args.push_back(dest_dir + binary_name + "/export_options.plist"); + export_args.push_back("-allowProvisioningUpdates"); + export_args.push_back("-exportPath"); + export_args.push_back(dest_dir); + err = OS::get_singleton()->execute("xcodebuild", export_args); + ERR_FAIL_COND_V(err, err); +#else + print_line(".ipa can only be built on macOS. Leaving Xcode project without building the package."); +#endif + + return OK; +} + +bool EditorExportPlatformIOS::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const { + String err; + bool valid = false; + + // Look for export templates (first official, and if defined custom templates). + + bool dvalid = exists_export_template("iphone.zip", &err); + bool rvalid = dvalid; // Both in the same ZIP. + + if (p_preset->get("custom_template/debug") != "") { + dvalid = FileAccess::exists(p_preset->get("custom_template/debug")); + if (!dvalid) { + err += TTR("Custom debug template not found.") + "\n"; + } + } + if (p_preset->get("custom_template/release") != "") { + rvalid = FileAccess::exists(p_preset->get("custom_template/release")); + if (!rvalid) { + err += TTR("Custom release template not found.") + "\n"; + } + } + + valid = dvalid || rvalid; + r_missing_templates = !valid; + + // Validate the rest of the configuration. + + String team_id = p_preset->get("application/app_store_team_id"); + if (team_id.length() == 0) { + err += TTR("App Store Team ID not specified - cannot configure the project.") + "\n"; + valid = false; + } + + String identifier = p_preset->get("application/bundle_identifier"); + String pn_err; + if (!is_package_name_valid(identifier, &pn_err)) { + err += TTR("Invalid Identifier:") + " " + pn_err + "\n"; + valid = false; + } + + for (uint64_t i = 0; i < (sizeof(icon_infos) / sizeof(icon_infos[0])); ++i) { + IconInfo info = icon_infos[i]; + String icon_path = p_preset->get(info.preset_key); + if (icon_path.length() == 0) { + if (info.is_required) { + err += TTR("Required icon is not specified in the preset.") + "\n"; + valid = false; + } + break; + } + } + + String etc_error = test_etc2_or_pvrtc(); + if (etc_error != String()) { + valid = false; + err += etc_error; + } + + if (!err.is_empty()) { + r_error = err; + } + + return valid; +} + +EditorExportPlatformIOS::EditorExportPlatformIOS() { + Ref<Image> img = memnew(Image(_iphone_logo)); + logo.instantiate(); + logo->create_from_image(img); + + plugins_changed.set(); + + check_for_changes_thread.start(_check_for_changes_poll_thread, this); +} + +EditorExportPlatformIOS::~EditorExportPlatformIOS() { + quit_request.set(); + check_for_changes_thread.wait_to_finish(); +} diff --git a/platform/iphone/export/export_plugin.h b/platform/iphone/export/export_plugin.h new file mode 100644 index 0000000000..8d3af6e057 --- /dev/null +++ b/platform/iphone/export/export_plugin.h @@ -0,0 +1,296 @@ +/*************************************************************************/ +/* export_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef IPHONE_EXPORT_PLUGIN_H +#define IPHONE_EXPORT_PLUGIN_H + +#include "core/config/project_settings.h" +#include "core/io/file_access.h" +#include "core/io/image_loader.h" +#include "core/io/marshalls.h" +#include "core/io/resource_saver.h" +#include "core/io/zip_io.h" +#include "core/os/os.h" +#include "core/templates/safe_refcount.h" +#include "core/version.h" +#include "editor/editor_export.h" +#include "editor/editor_node.h" +#include "editor/editor_settings.h" +#include "main/splash.gen.h" +#include "platform/iphone/logo.gen.h" +#include "string.h" + +#include "godot_plugin_config.h" + +#include <sys/stat.h> + +class EditorExportPlatformIOS : public EditorExportPlatform { + GDCLASS(EditorExportPlatformIOS, EditorExportPlatform); + + int version_code; + + Ref<ImageTexture> logo; + + // Plugins + SafeFlag plugins_changed; + Thread check_for_changes_thread; + SafeFlag quit_request; + Mutex plugins_lock; + Vector<PluginConfigIOS> plugins; + + typedef Error (*FileHandler)(String p_file, void *p_userdata); + static Error _walk_dir_recursive(DirAccess *p_da, FileHandler p_handler, void *p_userdata); + static Error _codesign(String p_file, void *p_userdata); + void _blend_and_rotate(Ref<Image> &p_dst, Ref<Image> &p_src, bool p_rot); + + struct IOSConfigData { + String pkg_name; + String binary_name; + String plist_content; + String architectures; + String linker_flags; + String cpp_code; + String modules_buildfile; + String modules_fileref; + String modules_buildphase; + String modules_buildgrp; + Vector<String> capabilities; + }; + struct ExportArchitecture { + String name; + bool is_default = false; + + ExportArchitecture() {} + + ExportArchitecture(String p_name, bool p_is_default) { + name = p_name; + is_default = p_is_default; + } + }; + + struct IOSExportAsset { + String exported_path; + bool is_framework = false; // framework is anything linked to the binary, otherwise it's a resource + bool should_embed = false; + }; + + String _get_additional_plist_content(); + String _get_linker_flags(); + String _get_cpp_code(); + void _fix_config_file(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &pfile, const IOSConfigData &p_config, bool p_debug); + Error _export_loading_screen_images(const Ref<EditorExportPreset> &p_preset, const String &p_dest_dir); + Error _export_loading_screen_file(const Ref<EditorExportPreset> &p_preset, const String &p_dest_dir); + Error _export_icons(const Ref<EditorExportPreset> &p_preset, const String &p_iconset_dir); + + Vector<ExportArchitecture> _get_supported_architectures(); + Vector<String> _get_preset_architectures(const Ref<EditorExportPreset> &p_preset); + + void _add_assets_to_project(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &p_project_data, const Vector<IOSExportAsset> &p_additional_assets); + Error _export_additional_assets(const String &p_out_dir, const Vector<String> &p_assets, bool p_is_framework, bool p_should_embed, Vector<IOSExportAsset> &r_exported_assets); + Error _copy_asset(const String &p_out_dir, const String &p_asset, const String *p_custom_file_name, bool p_is_framework, bool p_should_embed, Vector<IOSExportAsset> &r_exported_assets); + Error _export_additional_assets(const String &p_out_dir, const Vector<SharedObject> &p_libraries, Vector<IOSExportAsset> &r_exported_assets); + Error _export_ios_plugins(const Ref<EditorExportPreset> &p_preset, IOSConfigData &p_config_data, const String &dest_dir, Vector<IOSExportAsset> &r_exported_assets, bool p_debug); + + bool is_package_name_valid(const String &p_package, String *r_error = nullptr) const { + String pname = p_package; + + if (pname.length() == 0) { + if (r_error) { + *r_error = TTR("Identifier is missing."); + } + return false; + } + + for (int i = 0; i < pname.length(); i++) { + char32_t c = pname[i]; + if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-' || c == '.')) { + if (r_error) { + *r_error = vformat(TTR("The character '%s' is not allowed in Identifier."), String::chr(c)); + } + return false; + } + } + + return true; + } + + static void _check_for_changes_poll_thread(void *ud) { + EditorExportPlatformIOS *ea = (EditorExportPlatformIOS *)ud; + + while (!ea->quit_request.is_set()) { + // Nothing to do if we already know the plugins have changed. + if (!ea->plugins_changed.is_set()) { + MutexLock lock(ea->plugins_lock); + + Vector<PluginConfigIOS> loaded_plugins = get_plugins(); + + if (ea->plugins.size() != loaded_plugins.size()) { + ea->plugins_changed.set(); + } else { + for (int i = 0; i < ea->plugins.size(); i++) { + if (ea->plugins[i].name != loaded_plugins[i].name || ea->plugins[i].last_updated != loaded_plugins[i].last_updated) { + ea->plugins_changed.set(); + break; + } + } + } + } + + uint64_t wait = 3000000; + uint64_t time = OS::get_singleton()->get_ticks_usec(); + while (OS::get_singleton()->get_ticks_usec() - time < wait) { + OS::get_singleton()->delay_usec(300000); + + if (ea->quit_request.is_set()) { + break; + } + } + } + } + +protected: + virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) override; + virtual void get_export_options(List<ExportOption> *r_options) override; + +public: + virtual String get_name() const override { return "iOS"; } + virtual String get_os_name() const override { return "iOS"; } + virtual Ref<Texture2D> get_logo() const override { return logo; } + + virtual bool should_update_export_options() override { + bool export_options_changed = plugins_changed.is_set(); + if (export_options_changed) { + // don't clear unless we're reporting true, to avoid race + plugins_changed.clear(); + } + return export_options_changed; + } + + virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override { + List<String> list; + list.push_back("ipa"); + return list; + } + virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override; + + virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override; + + virtual void get_platform_features(List<String> *r_features) override { + r_features->push_back("mobile"); + r_features->push_back("iOS"); + } + + virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) override { + } + + EditorExportPlatformIOS(); + ~EditorExportPlatformIOS(); + + /// List the gdip files in the directory specified by the p_path parameter. + static Vector<String> list_plugin_config_files(const String &p_path, bool p_check_directories) { + Vector<String> dir_files; + DirAccessRef da = DirAccess::open(p_path); + if (da) { + da->list_dir_begin(); + while (true) { + String file = da->get_next(); + if (file.is_empty()) { + break; + } + + if (file == "." || file == "..") { + continue; + } + + if (da->current_is_hidden()) { + continue; + } + + if (da->current_is_dir()) { + if (p_check_directories) { + Vector<String> directory_files = list_plugin_config_files(p_path.plus_file(file), false); + for (int i = 0; i < directory_files.size(); ++i) { + dir_files.push_back(file.plus_file(directory_files[i])); + } + } + + continue; + } + + if (file.ends_with(PluginConfigIOS::PLUGIN_CONFIG_EXT)) { + dir_files.push_back(file); + } + } + da->list_dir_end(); + } + + return dir_files; + } + + static Vector<PluginConfigIOS> get_plugins() { + Vector<PluginConfigIOS> loaded_plugins; + + String plugins_dir = ProjectSettings::get_singleton()->get_resource_path().plus_file("ios/plugins"); + + if (DirAccess::exists(plugins_dir)) { + Vector<String> plugins_filenames = list_plugin_config_files(plugins_dir, true); + + if (!plugins_filenames.is_empty()) { + Ref<ConfigFile> config_file = memnew(ConfigFile); + for (int i = 0; i < plugins_filenames.size(); i++) { + PluginConfigIOS config = PluginConfigIOS::load_plugin_config(config_file, plugins_dir.plus_file(plugins_filenames[i])); + if (config.valid_config) { + loaded_plugins.push_back(config); + } else { + print_error("Invalid plugin config file " + plugins_filenames[i]); + } + } + } + } + + return loaded_plugins; + } + + static Vector<PluginConfigIOS> get_enabled_plugins(const Ref<EditorExportPreset> &p_presets) { + Vector<PluginConfigIOS> enabled_plugins; + Vector<PluginConfigIOS> all_plugins = get_plugins(); + for (int i = 0; i < all_plugins.size(); i++) { + PluginConfigIOS plugin = all_plugins[i]; + bool enabled = p_presets->get("plugins/" + plugin.name); + if (enabled) { + enabled_plugins.push_back(plugin); + } + } + + return enabled_plugins; + } +}; + +#endif diff --git a/platform/iphone/plugin/godot_plugin_config.h b/platform/iphone/export/godot_plugin_config.cpp index f9c5d7e51f..9d0324f41a 100644 --- a/platform/iphone/plugin/godot_plugin_config.h +++ b/platform/iphone/export/godot_plugin_config.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* godot_plugin_config.h */ +/* godot_plugin_config.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,92 +28,13 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef GODOT_PLUGIN_CONFIG_H -#define GODOT_PLUGIN_CONFIG_H - -#include "core/error/error_list.h" -#include "core/io/config_file.h" -#include "core/string/ustring.h" - -/* - The `config` section and fields are required and defined as follow: -- **name**: name of the plugin -- **binary**: path to static `.a` library - -The `dependencies` and fields are optional. -- **linked**: dependencies that should only be linked. -- **embedded**: dependencies that should be linked and embedded into application. -- **system**: system dependencies that should be linked. -- **capabilities**: capabilities that would be used for `UIRequiredDeviceCapabilities` options in Info.plist file. -- **files**: files that would be copied into application - -The `plist` section are optional. -- **key**: key and value that would be added in Info.plist file. - */ - -struct PluginConfigIOS { - inline static const char *PLUGIN_CONFIG_EXT = ".gdip"; - - inline static const char *CONFIG_SECTION = "config"; - inline static const char *CONFIG_NAME_KEY = "name"; - inline static const char *CONFIG_BINARY_KEY = "binary"; - inline static const char *CONFIG_INITIALIZE_KEY = "initialization"; - inline static const char *CONFIG_DEINITIALIZE_KEY = "deinitialization"; - - inline static const char *DEPENDENCIES_SECTION = "dependencies"; - inline static const char *DEPENDENCIES_LINKED_KEY = "linked"; - inline static const char *DEPENDENCIES_EMBEDDED_KEY = "embedded"; - inline static const char *DEPENDENCIES_SYSTEM_KEY = "system"; - inline static const char *DEPENDENCIES_CAPABILITIES_KEY = "capabilities"; - inline static const char *DEPENDENCIES_FILES_KEY = "files"; - inline static const char *DEPENDENCIES_LINKER_FLAGS = "linker_flags"; - - inline static const char *PLIST_SECTION = "plist"; - - enum PlistItemType { - UNKNOWN, - STRING, - INTEGER, - BOOLEAN, - RAW, - STRING_INPUT, - }; - - struct PlistItem { - PlistItemType type; - String value; - }; - - // Set to true when the config file is properly loaded. - bool valid_config = false; - bool supports_targets = false; - // Unix timestamp of last change to this plugin. - uint64_t last_updated = 0; - - // Required config section - String name; - String binary; - String initialization_method; - String deinitialization_method; - - // Optional dependencies section - Vector<String> linked_dependencies; - Vector<String> embedded_dependencies; - Vector<String> system_dependencies; - - Vector<String> files_to_copy; - Vector<String> capabilities; - - Vector<String> linker_flags; - - // Optional plist section - // String value is default value. - // Currently supports `string`, `boolean`, `integer`, `raw`, `string_input` types - // <name>:<type> = <value> - HashMap<String, PlistItem> plist; -}; - -static inline String resolve_local_dependency_path(String plugin_config_dir, String dependency_path) { +#include "godot_plugin_config.h" + +#include "core/config/project_settings.h" +#include "core/io/dir_access.h" +#include "core/io/file_access.h" + +String PluginConfigIOS::resolve_local_dependency_path(String plugin_config_dir, String dependency_path) { String absolute_path; if (dependency_path.is_empty()) { @@ -130,7 +51,7 @@ static inline String resolve_local_dependency_path(String plugin_config_dir, Str return absolute_path.replace(res_path, "res://"); } -static inline String resolve_system_dependency_path(String dependency_path) { +String PluginConfigIOS::resolve_system_dependency_path(String dependency_path) { String absolute_path; if (dependency_path.is_empty()) { @@ -146,7 +67,7 @@ static inline String resolve_system_dependency_path(String dependency_path) { return system_path.plus_file(dependency_path); } -static inline Vector<String> resolve_local_dependencies(String plugin_config_dir, Vector<String> p_paths) { +Vector<String> PluginConfigIOS::resolve_local_dependencies(String plugin_config_dir, Vector<String> p_paths) { Vector<String> paths; for (int i = 0; i < p_paths.size(); i++) { @@ -162,7 +83,7 @@ static inline Vector<String> resolve_local_dependencies(String plugin_config_dir return paths; } -static inline Vector<String> resolve_system_dependencies(Vector<String> p_paths) { +Vector<String> PluginConfigIOS::resolve_system_dependencies(Vector<String> p_paths) { Vector<String> paths; for (int i = 0; i < p_paths.size(); i++) { @@ -178,7 +99,7 @@ static inline Vector<String> resolve_system_dependencies(Vector<String> p_paths) return paths; } -static inline bool validate_plugin(PluginConfigIOS &plugin_config) { +bool PluginConfigIOS::validate_plugin(PluginConfigIOS &plugin_config) { bool valid_name = !plugin_config.name.is_empty(); bool valid_binary_name = !plugin_config.binary.is_empty(); bool valid_initialize = !plugin_config.initialization_method.is_empty(); @@ -213,7 +134,7 @@ static inline bool validate_plugin(PluginConfigIOS &plugin_config) { return plugin_config.valid_config; } -static inline String get_plugin_main_binary(PluginConfigIOS &plugin_config, bool p_debug) { +String PluginConfigIOS::get_plugin_main_binary(PluginConfigIOS &plugin_config, bool p_debug) { if (!plugin_config.supports_targets) { return plugin_config.binary; } @@ -226,7 +147,7 @@ static inline String get_plugin_main_binary(PluginConfigIOS &plugin_config, bool return plugin_binary_dir.plus_file(plugin_file); } -static inline uint64_t get_plugin_modification_time(const PluginConfigIOS &plugin_config, const String &config_path) { +uint64_t PluginConfigIOS::get_plugin_modification_time(const PluginConfigIOS &plugin_config, const String &config_path) { uint64_t last_updated = FileAccess::get_modified_time(config_path); if (!plugin_config.supports_targets) { @@ -245,7 +166,7 @@ static inline uint64_t get_plugin_modification_time(const PluginConfigIOS &plugi return last_updated; } -static inline PluginConfigIOS load_plugin_config(Ref<ConfigFile> config_file, const String &path) { +PluginConfigIOS PluginConfigIOS::load_plugin_config(Ref<ConfigFile> config_file, const String &path) { PluginConfigIOS plugin_config = {}; if (!config_file.is_valid()) { @@ -362,5 +283,3 @@ static inline PluginConfigIOS load_plugin_config(Ref<ConfigFile> config_file, co return plugin_config; } - -#endif // GODOT_PLUGIN_CONFIG_H diff --git a/platform/iphone/export/godot_plugin_config.h b/platform/iphone/export/godot_plugin_config.h new file mode 100644 index 0000000000..1add281627 --- /dev/null +++ b/platform/iphone/export/godot_plugin_config.h @@ -0,0 +1,132 @@ +/*************************************************************************/ +/* godot_plugin_config.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef IPHONE_GODOT_PLUGIN_CONFIG_H +#define IPHONE_GODOT_PLUGIN_CONFIG_H + +#include "core/error/error_list.h" +#include "core/io/config_file.h" +#include "core/string/ustring.h" + +/* + The `config` section and fields are required and defined as follow: +- **name**: name of the plugin +- **binary**: path to static `.a` library + +The `dependencies` and fields are optional. +- **linked**: dependencies that should only be linked. +- **embedded**: dependencies that should be linked and embedded into application. +- **system**: system dependencies that should be linked. +- **capabilities**: capabilities that would be used for `UIRequiredDeviceCapabilities` options in Info.plist file. +- **files**: files that would be copied into application + +The `plist` section are optional. +- **key**: key and value that would be added in Info.plist file. + */ + +struct PluginConfigIOS { + inline static const char *PLUGIN_CONFIG_EXT = ".gdip"; + + inline static const char *CONFIG_SECTION = "config"; + inline static const char *CONFIG_NAME_KEY = "name"; + inline static const char *CONFIG_BINARY_KEY = "binary"; + inline static const char *CONFIG_INITIALIZE_KEY = "initialization"; + inline static const char *CONFIG_DEINITIALIZE_KEY = "deinitialization"; + + inline static const char *DEPENDENCIES_SECTION = "dependencies"; + inline static const char *DEPENDENCIES_LINKED_KEY = "linked"; + inline static const char *DEPENDENCIES_EMBEDDED_KEY = "embedded"; + inline static const char *DEPENDENCIES_SYSTEM_KEY = "system"; + inline static const char *DEPENDENCIES_CAPABILITIES_KEY = "capabilities"; + inline static const char *DEPENDENCIES_FILES_KEY = "files"; + inline static const char *DEPENDENCIES_LINKER_FLAGS = "linker_flags"; + + inline static const char *PLIST_SECTION = "plist"; + + enum PlistItemType { + UNKNOWN, + STRING, + INTEGER, + BOOLEAN, + RAW, + STRING_INPUT, + }; + + struct PlistItem { + PlistItemType type; + String value; + }; + + // Set to true when the config file is properly loaded. + bool valid_config = false; + bool supports_targets = false; + // Unix timestamp of last change to this plugin. + uint64_t last_updated = 0; + + // Required config section + String name; + String binary; + String initialization_method; + String deinitialization_method; + + // Optional dependencies section + Vector<String> linked_dependencies; + Vector<String> embedded_dependencies; + Vector<String> system_dependencies; + + Vector<String> files_to_copy; + Vector<String> capabilities; + + Vector<String> linker_flags; + + // Optional plist section + // String value is default value. + // Currently supports `string`, `boolean`, `integer`, `raw`, `string_input` types + // <name>:<type> = <value> + HashMap<String, PlistItem> plist; + + static String resolve_local_dependency_path(String plugin_config_dir, String dependency_path); + + static String resolve_system_dependency_path(String dependency_path); + + static Vector<String> resolve_local_dependencies(String plugin_config_dir, Vector<String> p_paths); + + static Vector<String> resolve_system_dependencies(Vector<String> p_paths); + + static bool validate_plugin(PluginConfigIOS &plugin_config); + + static String get_plugin_main_binary(PluginConfigIOS &plugin_config, bool p_debug); + + static uint64_t get_plugin_modification_time(const PluginConfigIOS &plugin_config, const String &config_path); + + static PluginConfigIOS load_plugin_config(Ref<ConfigFile> config_file, const String &path); +}; + +#endif // GODOT_PLUGIN_CONFIG_H diff --git a/platform/javascript/export/export.cpp b/platform/javascript/export/export.cpp index bf4244eda4..889b0bbd02 100644 --- a/platform/javascript/export/export.cpp +++ b/platform/javascript/export/export.cpp @@ -28,971 +28,9 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "core/io/image_loader.h" -#include "core/io/stream_peer_ssl.h" -#include "core/io/tcp_server.h" -#include "core/io/zip_io.h" -#include "editor/editor_export.h" -#include "editor/editor_node.h" -#include "main/splash.gen.h" -#include "platform/javascript/logo.gen.h" -#include "platform/javascript/run_icon.gen.h" +#include "export.h" -class EditorHTTPServer : public RefCounted { -private: - Ref<TCPServer> server; - Map<String, String> mimes; - Ref<StreamPeerTCP> tcp; - Ref<StreamPeerSSL> ssl; - Ref<StreamPeer> peer; - Ref<CryptoKey> key; - Ref<X509Certificate> cert; - bool use_ssl = false; - uint64_t time = 0; - uint8_t req_buf[4096]; - int req_pos = 0; - - void _clear_client() { - peer = Ref<StreamPeer>(); - ssl = Ref<StreamPeerSSL>(); - tcp = Ref<StreamPeerTCP>(); - memset(req_buf, 0, sizeof(req_buf)); - time = 0; - req_pos = 0; - } - - void _set_internal_certs(Ref<Crypto> p_crypto) { - const String cache_path = EditorPaths::get_singleton()->get_cache_dir(); - const String key_path = cache_path.plus_file("html5_server.key"); - const String crt_path = cache_path.plus_file("html5_server.crt"); - bool regen = !FileAccess::exists(key_path) || !FileAccess::exists(crt_path); - if (!regen) { - key = Ref<CryptoKey>(CryptoKey::create()); - cert = Ref<X509Certificate>(X509Certificate::create()); - if (key->load(key_path) != OK || cert->load(crt_path) != OK) { - regen = true; - } - } - if (regen) { - key = p_crypto->generate_rsa(2048); - key->save(key_path); - cert = p_crypto->generate_self_signed_certificate(key, "CN=godot-debug.local,O=A Game Dev,C=XXA", "20140101000000", "20340101000000"); - cert->save(crt_path); - } - } - -public: - EditorHTTPServer() { - mimes["html"] = "text/html"; - mimes["js"] = "application/javascript"; - mimes["json"] = "application/json"; - mimes["pck"] = "application/octet-stream"; - mimes["png"] = "image/png"; - mimes["svg"] = "image/svg"; - mimes["wasm"] = "application/wasm"; - server.instantiate(); - stop(); - } - - void stop() { - server->stop(); - _clear_client(); - } - - Error listen(int p_port, IPAddress p_address, bool p_use_ssl, String p_ssl_key, String p_ssl_cert) { - use_ssl = p_use_ssl; - if (use_ssl) { - Ref<Crypto> crypto = Crypto::create(); - if (crypto.is_null()) { - return ERR_UNAVAILABLE; - } - if (!p_ssl_key.is_empty() && !p_ssl_cert.is_empty()) { - key = Ref<CryptoKey>(CryptoKey::create()); - Error err = key->load(p_ssl_key); - ERR_FAIL_COND_V(err != OK, err); - cert = Ref<X509Certificate>(X509Certificate::create()); - err = cert->load(p_ssl_cert); - ERR_FAIL_COND_V(err != OK, err); - } else { - _set_internal_certs(crypto); - } - } - return server->listen(p_port, p_address); - } - - bool is_listening() const { - return server->is_listening(); - } - - void _send_response() { - Vector<String> psa = String((char *)req_buf).split("\r\n"); - int len = psa.size(); - ERR_FAIL_COND_MSG(len < 4, "Not enough response headers, got: " + itos(len) + ", expected >= 4."); - - Vector<String> req = psa[0].split(" ", false); - ERR_FAIL_COND_MSG(req.size() < 2, "Invalid protocol or status code."); - - // Wrong protocol - ERR_FAIL_COND_MSG(req[0] != "GET" || req[2] != "HTTP/1.1", "Invalid method or HTTP version."); - - const int query_index = req[1].find_char('?'); - const String path = (query_index == -1) ? req[1] : req[1].substr(0, query_index); - - const String req_file = path.get_file(); - const String req_ext = path.get_extension(); - const String cache_path = EditorPaths::get_singleton()->get_cache_dir().plus_file("web"); - const String filepath = cache_path.plus_file(req_file); - - if (!mimes.has(req_ext) || !FileAccess::exists(filepath)) { - String s = "HTTP/1.1 404 Not Found\r\n"; - s += "Connection: Close\r\n"; - s += "\r\n"; - CharString cs = s.utf8(); - peer->put_data((const uint8_t *)cs.get_data(), cs.size() - 1); - return; - } - const String ctype = mimes[req_ext]; - - FileAccess *f = FileAccess::open(filepath, FileAccess::READ); - ERR_FAIL_COND(!f); - String s = "HTTP/1.1 200 OK\r\n"; - s += "Connection: Close\r\n"; - s += "Content-Type: " + ctype + "\r\n"; - s += "Access-Control-Allow-Origin: *\r\n"; - s += "Cross-Origin-Opener-Policy: same-origin\r\n"; - s += "Cross-Origin-Embedder-Policy: require-corp\r\n"; - s += "Cache-Control: no-store, max-age=0\r\n"; - s += "\r\n"; - CharString cs = s.utf8(); - Error err = peer->put_data((const uint8_t *)cs.get_data(), cs.size() - 1); - if (err != OK) { - memdelete(f); - ERR_FAIL(); - } - - while (true) { - uint8_t bytes[4096]; - uint64_t read = f->get_buffer(bytes, 4096); - if (read == 0) { - break; - } - err = peer->put_data(bytes, read); - if (err != OK) { - memdelete(f); - ERR_FAIL(); - } - } - memdelete(f); - } - - void poll() { - if (!server->is_listening()) { - return; - } - if (tcp.is_null()) { - if (!server->is_connection_available()) { - return; - } - tcp = server->take_connection(); - peer = tcp; - time = OS::get_singleton()->get_ticks_usec(); - } - if (OS::get_singleton()->get_ticks_usec() - time > 1000000) { - _clear_client(); - return; - } - if (tcp->get_status() != StreamPeerTCP::STATUS_CONNECTED) { - return; - } - - if (use_ssl) { - if (ssl.is_null()) { - ssl = Ref<StreamPeerSSL>(StreamPeerSSL::create()); - peer = ssl; - ssl->set_blocking_handshake_enabled(false); - if (ssl->accept_stream(tcp, key, cert) != OK) { - _clear_client(); - return; - } - } - ssl->poll(); - if (ssl->get_status() == StreamPeerSSL::STATUS_HANDSHAKING) { - // Still handshaking, keep waiting. - return; - } - if (ssl->get_status() != StreamPeerSSL::STATUS_CONNECTED) { - _clear_client(); - return; - } - } - - while (true) { - char *r = (char *)req_buf; - int l = req_pos - 1; - if (l > 3 && r[l] == '\n' && r[l - 1] == '\r' && r[l - 2] == '\n' && r[l - 3] == '\r') { - _send_response(); - _clear_client(); - return; - } - - int read = 0; - ERR_FAIL_COND(req_pos >= 4096); - Error err = peer->get_partial_data(&req_buf[req_pos], 1, read); - if (err != OK) { - // Got an error - _clear_client(); - return; - } else if (read != 1) { - // Busy, wait next poll - return; - } - req_pos += read; - } - } -}; - -class EditorExportPlatformJavaScript : public EditorExportPlatform { - GDCLASS(EditorExportPlatformJavaScript, EditorExportPlatform); - - Ref<ImageTexture> logo; - Ref<ImageTexture> run_icon; - Ref<ImageTexture> stop_icon; - int menu_options = 0; - - Ref<EditorHTTPServer> server; - bool server_quit = false; - Mutex server_lock; - Thread server_thread; - - enum ExportMode { - EXPORT_MODE_NORMAL = 0, - EXPORT_MODE_THREADS = 1, - EXPORT_MODE_GDNATIVE = 2, - }; - - String _get_template_name(ExportMode p_mode, bool p_debug) const { - String name = "webassembly"; - switch (p_mode) { - case EXPORT_MODE_THREADS: - name += "_threads"; - break; - case EXPORT_MODE_GDNATIVE: - name += "_gdnative"; - break; - default: - break; - } - if (p_debug) { - name += "_debug.zip"; - } else { - name += "_release.zip"; - } - return name; - } - - Ref<Image> _get_project_icon() const { - Ref<Image> icon; - icon.instantiate(); - const String icon_path = String(GLOBAL_GET("application/config/icon")).strip_edges(); - if (icon_path.is_empty() || ImageLoader::load_image(icon_path, icon) != OK) { - return EditorNode::get_singleton()->get_editor_theme()->get_icon("DefaultProjectIcon", "EditorIcons")->get_image(); - } - return icon; - } - - Ref<Image> _get_project_splash() const { - Ref<Image> splash; - splash.instantiate(); - const String splash_path = String(GLOBAL_GET("application/boot_splash/image")).strip_edges(); - if (splash_path.is_empty() || ImageLoader::load_image(splash_path, splash) != OK) { - return Ref<Image>(memnew(Image(boot_splash_png))); - } - return splash; - } - - Error _extract_template(const String &p_template, const String &p_dir, const String &p_name, bool pwa); - void _replace_strings(Map<String, String> p_replaces, Vector<uint8_t> &r_template); - void _fix_html(Vector<uint8_t> &p_html, const Ref<EditorExportPreset> &p_preset, const String &p_name, bool p_debug, int p_flags, const Vector<SharedObject> p_shared_objects, const Dictionary &p_file_sizes); - Error _add_manifest_icon(const String &p_path, const String &p_icon, int p_size, Array &r_arr); - Error _build_pwa(const Ref<EditorExportPreset> &p_preset, const String p_path, const Vector<SharedObject> &p_shared_objects); - Error _write_or_error(const uint8_t *p_content, int p_len, String p_path); - - static void _server_thread_poll(void *data); - -public: - virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) override; - - virtual void get_export_options(List<ExportOption> *r_options) override; - - virtual String get_name() const override; - virtual String get_os_name() const override; - virtual Ref<Texture2D> get_logo() const override; - - virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override; - virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override; - virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override; - - virtual bool poll_export() override; - virtual int get_options_count() const override; - virtual String get_option_label(int p_index) const override { return p_index ? TTR("Stop HTTP Server") : TTR("Run in Browser"); } - virtual String get_option_tooltip(int p_index) const override { return p_index ? TTR("Stop HTTP Server") : TTR("Run exported HTML in the system's default browser."); } - virtual Ref<ImageTexture> get_option_icon(int p_index) const override; - virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_option, int p_debug_flags) override; - virtual Ref<Texture2D> get_run_icon() const override; - - virtual void get_platform_features(List<String> *r_features) override { - r_features->push_back("web"); - r_features->push_back(get_os_name()); - } - - virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) override { - } - - String get_debug_protocol() const override { return "ws://"; } - - EditorExportPlatformJavaScript(); - ~EditorExportPlatformJavaScript(); -}; - -Error EditorExportPlatformJavaScript::_extract_template(const String &p_template, const String &p_dir, const String &p_name, bool pwa) { - FileAccess *src_f = nullptr; - zlib_filefunc_def io = zipio_create_io_from_file(&src_f); - unzFile pkg = unzOpen2(p_template.utf8().get_data(), &io); - - if (!pkg) { - EditorNode::get_singleton()->show_warning(TTR("Could not open template for export:") + "\n" + p_template); - return ERR_FILE_NOT_FOUND; - } - - if (unzGoToFirstFile(pkg) != UNZ_OK) { - EditorNode::get_singleton()->show_warning(TTR("Invalid export template:") + "\n" + p_template); - unzClose(pkg); - return ERR_FILE_CORRUPT; - } - - do { - //get filename - unz_file_info info; - char fname[16384]; - unzGetCurrentFileInfo(pkg, &info, fname, 16384, nullptr, 0, nullptr, 0); - - String file = fname; - - // Skip service worker and offline page if not exporting pwa. - if (!pwa && (file == "godot.service.worker.js" || file == "godot.offline.html")) { - continue; - } - Vector<uint8_t> data; - data.resize(info.uncompressed_size); - - //read - unzOpenCurrentFile(pkg); - unzReadCurrentFile(pkg, data.ptrw(), data.size()); - unzCloseCurrentFile(pkg); - - //write - String dst = p_dir.plus_file(file.replace("godot", p_name)); - FileAccess *f = FileAccess::open(dst, FileAccess::WRITE); - if (!f) { - EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + dst); - unzClose(pkg); - return ERR_FILE_CANT_WRITE; - } - f->store_buffer(data.ptr(), data.size()); - memdelete(f); - - } while (unzGoToNextFile(pkg) == UNZ_OK); - unzClose(pkg); - return OK; -} - -Error EditorExportPlatformJavaScript::_write_or_error(const uint8_t *p_content, int p_size, String p_path) { - FileAccess *f = FileAccess::open(p_path, FileAccess::WRITE); - if (!f) { - EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + p_path); - return ERR_FILE_CANT_WRITE; - } - f->store_buffer(p_content, p_size); - memdelete(f); - return OK; -} - -void EditorExportPlatformJavaScript::_replace_strings(Map<String, String> p_replaces, Vector<uint8_t> &r_template) { - String str_template = String::utf8(reinterpret_cast<const char *>(r_template.ptr()), r_template.size()); - String out; - Vector<String> lines = str_template.split("\n"); - for (int i = 0; i < lines.size(); i++) { - String current_line = lines[i]; - for (Map<String, String>::Element *E = p_replaces.front(); E; E = E->next()) { - current_line = current_line.replace(E->key(), E->get()); - } - out += current_line + "\n"; - } - CharString cs = out.utf8(); - r_template.resize(cs.length()); - for (int i = 0; i < cs.length(); i++) { - r_template.write[i] = cs[i]; - } -} - -void EditorExportPlatformJavaScript::_fix_html(Vector<uint8_t> &p_html, const Ref<EditorExportPreset> &p_preset, const String &p_name, bool p_debug, int p_flags, const Vector<SharedObject> p_shared_objects, const Dictionary &p_file_sizes) { - // Engine.js config - Dictionary config; - Array libs; - for (int i = 0; i < p_shared_objects.size(); i++) { - libs.push_back(p_shared_objects[i].path.get_file()); - } - Vector<String> flags; - gen_export_flags(flags, p_flags & (~DEBUG_FLAG_DUMB_CLIENT)); - Array args; - for (int i = 0; i < flags.size(); i++) { - args.push_back(flags[i]); - } - config["canvasResizePolicy"] = p_preset->get("html/canvas_resize_policy"); - config["experimentalVK"] = p_preset->get("html/experimental_virtual_keyboard"); - config["focusCanvas"] = p_preset->get("html/focus_canvas_on_start"); - config["gdnativeLibs"] = libs; - config["executable"] = p_name; - config["args"] = args; - config["fileSizes"] = p_file_sizes; - - String head_include; - if (p_preset->get("html/export_icon")) { - head_include += "<link id='-gd-engine-icon' rel='icon' type='image/png' href='" + p_name + ".icon.png' />\n"; - head_include += "<link rel='apple-touch-icon' href='" + p_name + ".apple-touch-icon.png'/>\n"; - } - if (p_preset->get("progressive_web_app/enabled")) { - head_include += "<link rel='manifest' href='" + p_name + ".manifest.json'>\n"; - head_include += "<script type='application/javascript'>window.addEventListener('load', () => {if ('serviceWorker' in navigator) {navigator.serviceWorker.register('" + - p_name + ".service.worker.js');}});</script>\n"; - } - - // Replaces HTML string - const String str_config = Variant(config).to_json_string(); - const String custom_head_include = p_preset->get("html/head_include"); - Map<String, String> replaces; - replaces["$GODOT_URL"] = p_name + ".js"; - replaces["$GODOT_PROJECT_NAME"] = ProjectSettings::get_singleton()->get_setting("application/config/name"); - replaces["$GODOT_HEAD_INCLUDE"] = head_include + custom_head_include; - replaces["$GODOT_CONFIG"] = str_config; - _replace_strings(replaces, p_html); -} - -Error EditorExportPlatformJavaScript::_add_manifest_icon(const String &p_path, const String &p_icon, int p_size, Array &r_arr) { - const String name = p_path.get_file().get_basename(); - const String icon_name = vformat("%s.%dx%d.png", name, p_size, p_size); - const String icon_dest = p_path.get_base_dir().plus_file(icon_name); - - Ref<Image> icon; - if (!p_icon.is_empty()) { - icon.instantiate(); - const Error err = ImageLoader::load_image(p_icon, icon); - if (err != OK) { - EditorNode::get_singleton()->show_warning(TTR("Could not read file:") + "\n" + p_icon); - return err; - } - if (icon->get_width() != p_size || icon->get_height() != p_size) { - icon->resize(p_size, p_size); - } - } else { - icon = _get_project_icon(); - icon->resize(p_size, p_size); - } - const Error err = icon->save_png(icon_dest); - if (err != OK) { - EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + icon_dest); - return err; - } - Dictionary icon_dict; - icon_dict["sizes"] = vformat("%dx%d", p_size, p_size); - icon_dict["type"] = "image/png"; - icon_dict["src"] = icon_name; - r_arr.push_back(icon_dict); - return err; -} - -Error EditorExportPlatformJavaScript::_build_pwa(const Ref<EditorExportPreset> &p_preset, const String p_path, const Vector<SharedObject> &p_shared_objects) { - // Service worker - const String dir = p_path.get_base_dir(); - const String name = p_path.get_file().get_basename(); - const ExportMode mode = (ExportMode)(int)p_preset->get("variant/export_type"); - Map<String, String> replaces; - replaces["@GODOT_VERSION@"] = "1"; - replaces["@GODOT_NAME@"] = name; - replaces["@GODOT_OFFLINE_PAGE@"] = name + ".offline.html"; - Array files; - replaces["@GODOT_OPT_CACHE@"] = Variant(files).to_json_string(); - files.push_back(name + ".html"); - files.push_back(name + ".js"); - files.push_back(name + ".wasm"); - files.push_back(name + ".pck"); - files.push_back(name + ".offline.html"); - if (p_preset->get("html/export_icon")) { - files.push_back(name + ".icon.png"); - files.push_back(name + ".apple-touch-icon.png"); - } - if (mode == EXPORT_MODE_THREADS) { - files.push_back(name + ".worker.js"); - files.push_back(name + ".audio.worklet.js"); - } else if (mode == EXPORT_MODE_GDNATIVE) { - files.push_back(name + ".side.wasm"); - for (int i = 0; i < p_shared_objects.size(); i++) { - files.push_back(p_shared_objects[i].path.get_file()); - } - } - replaces["@GODOT_CACHE@"] = Variant(files).to_json_string(); - - const String sw_path = dir.plus_file(name + ".service.worker.js"); - Vector<uint8_t> sw; - { - FileAccess *f = FileAccess::open(sw_path, FileAccess::READ); - if (!f) { - EditorNode::get_singleton()->show_warning(TTR("Could not read file:") + "\n" + sw_path); - return ERR_FILE_CANT_READ; - } - sw.resize(f->get_length()); - f->get_buffer(sw.ptrw(), sw.size()); - memdelete(f); - f = nullptr; - } - _replace_strings(replaces, sw); - Error err = _write_or_error(sw.ptr(), sw.size(), dir.plus_file(name + ".service.worker.js")); - if (err != OK) { - return err; - } - - // Custom offline page - const String offline_page = p_preset->get("progressive_web_app/offline_page"); - if (!offline_page.is_empty()) { - DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - const String offline_dest = dir.plus_file(name + ".offline.html"); - err = da->copy(ProjectSettings::get_singleton()->globalize_path(offline_page), offline_dest); - if (err != OK) { - EditorNode::get_singleton()->show_warning(TTR("Could not read file:") + "\n" + offline_dest); - return err; - } - } - - // Manifest - const char *modes[4] = { "fullscreen", "standalone", "minimal-ui", "browser" }; - const char *orientations[3] = { "any", "landscape", "portrait" }; - const int display = CLAMP(int(p_preset->get("progressive_web_app/display")), 0, 4); - const int orientation = CLAMP(int(p_preset->get("progressive_web_app/orientation")), 0, 3); - - Dictionary manifest; - String proj_name = ProjectSettings::get_singleton()->get_setting("application/config/name"); - if (proj_name.is_empty()) { - proj_name = "Godot Game"; - } - manifest["name"] = proj_name; - manifest["start_url"] = "./" + name + ".html"; - manifest["display"] = String::utf8(modes[display]); - manifest["orientation"] = String::utf8(orientations[orientation]); - manifest["background_color"] = "#" + p_preset->get("progressive_web_app/background_color").operator Color().to_html(false); - - Array icons_arr; - const String icon144_path = p_preset->get("progressive_web_app/icon_144x144"); - err = _add_manifest_icon(p_path, icon144_path, 144, icons_arr); - if (err != OK) { - return err; - } - const String icon180_path = p_preset->get("progressive_web_app/icon_180x180"); - err = _add_manifest_icon(p_path, icon180_path, 180, icons_arr); - if (err != OK) { - return err; - } - const String icon512_path = p_preset->get("progressive_web_app/icon_512x512"); - err = _add_manifest_icon(p_path, icon512_path, 512, icons_arr); - if (err != OK) { - return err; - } - manifest["icons"] = icons_arr; - - CharString cs = Variant(manifest).to_json_string().utf8(); - err = _write_or_error((const uint8_t *)cs.get_data(), cs.length(), dir.plus_file(name + ".manifest.json")); - if (err != OK) { - return err; - } - - return OK; -} - -void EditorExportPlatformJavaScript::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) { - if (p_preset->get("vram_texture_compression/for_desktop")) { - r_features->push_back("s3tc"); - } - - if (p_preset->get("vram_texture_compression/for_mobile")) { - String driver = ProjectSettings::get_singleton()->get("rendering/driver/driver_name"); - if (driver == "GLES2") { - r_features->push_back("etc"); - } else if (driver == "Vulkan") { - // FIXME: Review if this is correct. - r_features->push_back("etc2"); - } - } - ExportMode mode = (ExportMode)(int)p_preset->get("variant/export_type"); - if (mode == EXPORT_MODE_THREADS) { - r_features->push_back("threads"); - } else if (mode == EXPORT_MODE_GDNATIVE) { - r_features->push_back("wasm32"); - } -} - -void EditorExportPlatformJavaScript::get_export_options(List<ExportOption> *r_options) { - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), "")); - - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "variant/export_type", PROPERTY_HINT_ENUM, "Regular,Threads,GDNative"), 0)); // Export type. - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "vram_texture_compression/for_desktop"), true)); // S3TC - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "vram_texture_compression/for_mobile"), false)); // ETC or ETC2, depending on renderer - - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "html/export_icon"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "html/custom_html_shell", PROPERTY_HINT_FILE, "*.html"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "html/head_include", PROPERTY_HINT_MULTILINE_TEXT), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "html/canvas_resize_policy", PROPERTY_HINT_ENUM, "None,Project,Adaptive"), 2)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "html/focus_canvas_on_start"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "html/experimental_virtual_keyboard"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "progressive_web_app/enabled"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "progressive_web_app/offline_page", PROPERTY_HINT_FILE, "*.html"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "progressive_web_app/display", PROPERTY_HINT_ENUM, "Fullscreen,Standalone,Minimal UI,Browser"), 1)); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "progressive_web_app/orientation", PROPERTY_HINT_ENUM, "Any,Landscape,Portrait"), 0)); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "progressive_web_app/icon_144x144", PROPERTY_HINT_FILE, "*.png,*.webp,*.svg,*.svgz"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "progressive_web_app/icon_180x180", PROPERTY_HINT_FILE, "*.png,*.webp,*.svg,*.svgz"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "progressive_web_app/icon_512x512", PROPERTY_HINT_FILE, "*.png,*.webp,*.svg,*.svgz"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::COLOR, "progressive_web_app/background_color", PROPERTY_HINT_COLOR_NO_ALPHA), Color())); -} - -String EditorExportPlatformJavaScript::get_name() const { - return "HTML5"; -} - -String EditorExportPlatformJavaScript::get_os_name() const { - return "HTML5"; -} - -Ref<Texture2D> EditorExportPlatformJavaScript::get_logo() const { - return logo; -} - -bool EditorExportPlatformJavaScript::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const { - String err; - bool valid = false; - ExportMode mode = (ExportMode)(int)p_preset->get("variant/export_type"); - - // Look for export templates (first official, and if defined custom templates). - bool dvalid = exists_export_template(_get_template_name(mode, true), &err); - bool rvalid = exists_export_template(_get_template_name(mode, false), &err); - - if (p_preset->get("custom_template/debug") != "") { - dvalid = FileAccess::exists(p_preset->get("custom_template/debug")); - if (!dvalid) { - err += TTR("Custom debug template not found.") + "\n"; - } - } - if (p_preset->get("custom_template/release") != "") { - rvalid = FileAccess::exists(p_preset->get("custom_template/release")); - if (!rvalid) { - err += TTR("Custom release template not found.") + "\n"; - } - } - - valid = dvalid || rvalid; - r_missing_templates = !valid; - - // Validate the rest of the configuration. - - if (p_preset->get("vram_texture_compression/for_mobile")) { - String etc_error = test_etc2(); - if (etc_error != String()) { - valid = false; - err += etc_error; - } - } - - if (!err.is_empty()) { - r_error = err; - } - - return valid; -} - -List<String> EditorExportPlatformJavaScript::get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const { - List<String> list; - list.push_back("html"); - return list; -} - -Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { - ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags); - - const String custom_debug = p_preset->get("custom_template/debug"); - const String custom_release = p_preset->get("custom_template/release"); - const String custom_html = p_preset->get("html/custom_html_shell"); - const bool export_icon = p_preset->get("html/export_icon"); - const bool pwa = p_preset->get("progressive_web_app/enabled"); - - const String base_dir = p_path.get_base_dir(); - const String base_path = p_path.get_basename(); - const String base_name = p_path.get_file().get_basename(); - - // Find the correct template - String template_path = p_debug ? custom_debug : custom_release; - template_path = template_path.strip_edges(); - if (template_path == String()) { - ExportMode mode = (ExportMode)(int)p_preset->get("variant/export_type"); - template_path = find_export_template(_get_template_name(mode, p_debug)); - } - - if (!DirAccess::exists(base_dir)) { - return ERR_FILE_BAD_PATH; - } - - if (template_path != String() && !FileAccess::exists(template_path)) { - EditorNode::get_singleton()->show_warning(TTR("Template file not found:") + "\n" + template_path); - return ERR_FILE_NOT_FOUND; - } - - // Export pck and shared objects - Vector<SharedObject> shared_objects; - String pck_path = base_path + ".pck"; - Error error = save_pack(p_preset, pck_path, &shared_objects); - if (error != OK) { - EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + pck_path); - return error; - } - DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - for (int i = 0; i < shared_objects.size(); i++) { - String dst = base_dir.plus_file(shared_objects[i].path.get_file()); - error = da->copy(shared_objects[i].path, dst); - if (error != OK) { - EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + shared_objects[i].path.get_file()); - memdelete(da); - return error; - } - } - memdelete(da); - da = nullptr; - - // Extract templates. - error = _extract_template(template_path, base_dir, base_name, pwa); - if (error) { - return error; - } - - // Parse generated file sizes (pck and wasm, to help show a meaningful loading bar). - Dictionary file_sizes; - FileAccess *f = nullptr; - f = FileAccess::open(pck_path, FileAccess::READ); - if (f) { - file_sizes[pck_path.get_file()] = (uint64_t)f->get_length(); - memdelete(f); - f = nullptr; - } - f = FileAccess::open(base_path + ".wasm", FileAccess::READ); - if (f) { - file_sizes[base_name + ".wasm"] = (uint64_t)f->get_length(); - memdelete(f); - f = nullptr; - } - - // Read the HTML shell file (custom or from template). - const String html_path = custom_html.is_empty() ? base_path + ".html" : custom_html; - Vector<uint8_t> html; - f = FileAccess::open(html_path, FileAccess::READ); - if (!f) { - EditorNode::get_singleton()->show_warning(TTR("Could not read HTML shell:") + "\n" + html_path); - return ERR_FILE_CANT_READ; - } - html.resize(f->get_length()); - f->get_buffer(html.ptrw(), html.size()); - memdelete(f); - f = nullptr; - - // Generate HTML file with replaced strings. - _fix_html(html, p_preset, base_name, p_debug, p_flags, shared_objects, file_sizes); - Error err = _write_or_error(html.ptr(), html.size(), p_path); - if (err != OK) { - return err; - } - html.resize(0); - - // Export splash (why?) - Ref<Image> splash = _get_project_splash(); - const String splash_png_path = base_path + ".png"; - if (splash->save_png(splash_png_path) != OK) { - EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + splash_png_path); - return ERR_FILE_CANT_WRITE; - } - - // Save a favicon that can be accessed without waiting for the project to finish loading. - // This way, the favicon can be displayed immediately when loading the page. - if (export_icon) { - Ref<Image> favicon = _get_project_icon(); - const String favicon_png_path = base_path + ".icon.png"; - if (favicon->save_png(favicon_png_path) != OK) { - EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + favicon_png_path); - return ERR_FILE_CANT_WRITE; - } - favicon->resize(180, 180); - const String apple_icon_png_path = base_path + ".apple-touch-icon.png"; - if (favicon->save_png(apple_icon_png_path) != OK) { - EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + apple_icon_png_path); - return ERR_FILE_CANT_WRITE; - } - } - - // Generate the PWA worker and manifest - if (pwa) { - err = _build_pwa(p_preset, p_path, shared_objects); - if (err != OK) { - return err; - } - } - - return OK; -} - -bool EditorExportPlatformJavaScript::poll_export() { - Ref<EditorExportPreset> preset; - - for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) { - Ref<EditorExportPreset> ep = EditorExport::get_singleton()->get_export_preset(i); - if (ep->is_runnable() && ep->get_platform() == this) { - preset = ep; - break; - } - } - - int prev = menu_options; - menu_options = preset.is_valid(); - if (server->is_listening()) { - if (menu_options == 0) { - MutexLock lock(server_lock); - server->stop(); - } else { - menu_options += 1; - } - } - return menu_options != prev; -} - -Ref<ImageTexture> EditorExportPlatformJavaScript::get_option_icon(int p_index) const { - return p_index == 1 ? stop_icon : EditorExportPlatform::get_option_icon(p_index); -} - -int EditorExportPlatformJavaScript::get_options_count() const { - return menu_options; -} - -Error EditorExportPlatformJavaScript::run(const Ref<EditorExportPreset> &p_preset, int p_option, int p_debug_flags) { - if (p_option == 1) { - MutexLock lock(server_lock); - server->stop(); - return OK; - } - - const String dest = EditorPaths::get_singleton()->get_cache_dir().plus_file("web"); - DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - if (!da->dir_exists(dest)) { - Error err = da->make_dir_recursive(dest); - if (err != OK) { - EditorNode::get_singleton()->show_warning(TTR("Could not create HTTP server directory:") + "\n" + dest); - return err; - } - } - const String basepath = dest.plus_file("tmp_js_export"); - Error err = export_project(p_preset, true, basepath + ".html", p_debug_flags); - if (err != OK) { - // Export generates several files, clean them up on failure. - DirAccess::remove_file_or_error(basepath + ".html"); - DirAccess::remove_file_or_error(basepath + ".offline.html"); - DirAccess::remove_file_or_error(basepath + ".js"); - DirAccess::remove_file_or_error(basepath + ".worker.js"); - DirAccess::remove_file_or_error(basepath + ".audio.worklet.js"); - DirAccess::remove_file_or_error(basepath + ".service.worker.js"); - DirAccess::remove_file_or_error(basepath + ".pck"); - DirAccess::remove_file_or_error(basepath + ".png"); - DirAccess::remove_file_or_error(basepath + ".side.wasm"); - DirAccess::remove_file_or_error(basepath + ".wasm"); - DirAccess::remove_file_or_error(basepath + ".icon.png"); - DirAccess::remove_file_or_error(basepath + ".apple-touch-icon.png"); - return err; - } - - const uint16_t bind_port = EDITOR_GET("export/web/http_port"); - // Resolve host if needed. - const String bind_host = EDITOR_GET("export/web/http_host"); - IPAddress bind_ip; - if (bind_host.is_valid_ip_address()) { - bind_ip = bind_host; - } else { - bind_ip = IP::get_singleton()->resolve_hostname(bind_host); - } - ERR_FAIL_COND_V_MSG(!bind_ip.is_valid(), ERR_INVALID_PARAMETER, "Invalid editor setting 'export/web/http_host': '" + bind_host + "'. Try using '127.0.0.1'."); - - const bool use_ssl = EDITOR_GET("export/web/use_ssl"); - const String ssl_key = EDITOR_GET("export/web/ssl_key"); - const String ssl_cert = EDITOR_GET("export/web/ssl_certificate"); - - // Restart server. - { - MutexLock lock(server_lock); - - server->stop(); - err = server->listen(bind_port, bind_ip, use_ssl, ssl_key, ssl_cert); - } - if (err != OK) { - EditorNode::get_singleton()->show_warning(TTR("Error starting HTTP server:") + "\n" + itos(err)); - return err; - } - - OS::get_singleton()->shell_open(String((use_ssl ? "https://" : "http://") + bind_host + ":" + itos(bind_port) + "/tmp_js_export.html")); - // FIXME: Find out how to clean up export files after running the successfully - // exported game. Might not be trivial. - return OK; -} - -Ref<Texture2D> EditorExportPlatformJavaScript::get_run_icon() const { - return run_icon; -} - -void EditorExportPlatformJavaScript::_server_thread_poll(void *data) { - EditorExportPlatformJavaScript *ej = (EditorExportPlatformJavaScript *)data; - while (!ej->server_quit) { - OS::get_singleton()->delay_usec(1000); - { - MutexLock lock(ej->server_lock); - ej->server->poll(); - } - } -} - -EditorExportPlatformJavaScript::EditorExportPlatformJavaScript() { - server.instantiate(); - server_thread.start(_server_thread_poll, this); - - Ref<Image> img = memnew(Image(_javascript_logo)); - logo.instantiate(); - logo->create_from_image(img); - - img = Ref<Image>(memnew(Image(_javascript_run_icon))); - run_icon.instantiate(); - run_icon->create_from_image(img); - - Ref<Theme> theme = EditorNode::get_singleton()->get_editor_theme(); - if (theme.is_valid()) { - stop_icon = theme->get_icon("Stop", "EditorIcons"); - } else { - stop_icon.instantiate(); - } -} - -EditorExportPlatformJavaScript::~EditorExportPlatformJavaScript() { - server->stop(); - server_quit = true; - server_thread.wait_to_finish(); -} +#include "export_plugin.h" void register_javascript_exporter() { EDITOR_DEF("export/web/http_host", "localhost"); diff --git a/platform/javascript/export/export.h b/platform/javascript/export/export.h index e641339f55..8e8161b547 100644 --- a/platform/javascript/export/export.h +++ b/platform/javascript/export/export.h @@ -28,4 +28,9 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ +#ifndef JAVASCRIPT_EXPORT_H +#define JAVASCRIPT_EXPORT_H + void register_javascript_exporter(); + +#endif diff --git a/platform/javascript/export/export_plugin.cpp b/platform/javascript/export/export_plugin.cpp new file mode 100644 index 0000000000..5d285a6e60 --- /dev/null +++ b/platform/javascript/export/export_plugin.cpp @@ -0,0 +1,671 @@ +/*************************************************************************/ +/* export_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "export_plugin.h" + +Error EditorExportPlatformJavaScript::_extract_template(const String &p_template, const String &p_dir, const String &p_name, bool pwa) { + FileAccess *src_f = nullptr; + zlib_filefunc_def io = zipio_create_io_from_file(&src_f); + unzFile pkg = unzOpen2(p_template.utf8().get_data(), &io); + + if (!pkg) { + EditorNode::get_singleton()->show_warning(TTR("Could not open template for export:") + "\n" + p_template); + return ERR_FILE_NOT_FOUND; + } + + if (unzGoToFirstFile(pkg) != UNZ_OK) { + EditorNode::get_singleton()->show_warning(TTR("Invalid export template:") + "\n" + p_template); + unzClose(pkg); + return ERR_FILE_CORRUPT; + } + + do { + //get filename + unz_file_info info; + char fname[16384]; + unzGetCurrentFileInfo(pkg, &info, fname, 16384, nullptr, 0, nullptr, 0); + + String file = fname; + + // Skip service worker and offline page if not exporting pwa. + if (!pwa && (file == "godot.service.worker.js" || file == "godot.offline.html")) { + continue; + } + Vector<uint8_t> data; + data.resize(info.uncompressed_size); + + //read + unzOpenCurrentFile(pkg); + unzReadCurrentFile(pkg, data.ptrw(), data.size()); + unzCloseCurrentFile(pkg); + + //write + String dst = p_dir.plus_file(file.replace("godot", p_name)); + FileAccess *f = FileAccess::open(dst, FileAccess::WRITE); + if (!f) { + EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + dst); + unzClose(pkg); + return ERR_FILE_CANT_WRITE; + } + f->store_buffer(data.ptr(), data.size()); + memdelete(f); + + } while (unzGoToNextFile(pkg) == UNZ_OK); + unzClose(pkg); + return OK; +} + +Error EditorExportPlatformJavaScript::_write_or_error(const uint8_t *p_content, int p_size, String p_path) { + FileAccess *f = FileAccess::open(p_path, FileAccess::WRITE); + if (!f) { + EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + p_path); + return ERR_FILE_CANT_WRITE; + } + f->store_buffer(p_content, p_size); + memdelete(f); + return OK; +} + +void EditorExportPlatformJavaScript::_replace_strings(Map<String, String> p_replaces, Vector<uint8_t> &r_template) { + String str_template = String::utf8(reinterpret_cast<const char *>(r_template.ptr()), r_template.size()); + String out; + Vector<String> lines = str_template.split("\n"); + for (int i = 0; i < lines.size(); i++) { + String current_line = lines[i]; + for (Map<String, String>::Element *E = p_replaces.front(); E; E = E->next()) { + current_line = current_line.replace(E->key(), E->get()); + } + out += current_line + "\n"; + } + CharString cs = out.utf8(); + r_template.resize(cs.length()); + for (int i = 0; i < cs.length(); i++) { + r_template.write[i] = cs[i]; + } +} + +void EditorExportPlatformJavaScript::_fix_html(Vector<uint8_t> &p_html, const Ref<EditorExportPreset> &p_preset, const String &p_name, bool p_debug, int p_flags, const Vector<SharedObject> p_shared_objects, const Dictionary &p_file_sizes) { + // Engine.js config + Dictionary config; + Array libs; + for (int i = 0; i < p_shared_objects.size(); i++) { + libs.push_back(p_shared_objects[i].path.get_file()); + } + Vector<String> flags; + gen_export_flags(flags, p_flags & (~DEBUG_FLAG_DUMB_CLIENT)); + Array args; + for (int i = 0; i < flags.size(); i++) { + args.push_back(flags[i]); + } + config["canvasResizePolicy"] = p_preset->get("html/canvas_resize_policy"); + config["experimentalVK"] = p_preset->get("html/experimental_virtual_keyboard"); + config["focusCanvas"] = p_preset->get("html/focus_canvas_on_start"); + config["gdnativeLibs"] = libs; + config["executable"] = p_name; + config["args"] = args; + config["fileSizes"] = p_file_sizes; + + String head_include; + if (p_preset->get("html/export_icon")) { + head_include += "<link id='-gd-engine-icon' rel='icon' type='image/png' href='" + p_name + ".icon.png' />\n"; + head_include += "<link rel='apple-touch-icon' href='" + p_name + ".apple-touch-icon.png'/>\n"; + } + if (p_preset->get("progressive_web_app/enabled")) { + head_include += "<link rel='manifest' href='" + p_name + ".manifest.json'>\n"; + head_include += "<script type='application/javascript'>window.addEventListener('load', () => {if ('serviceWorker' in navigator) {navigator.serviceWorker.register('" + + p_name + ".service.worker.js');}});</script>\n"; + } + + // Replaces HTML string + const String str_config = Variant(config).to_json_string(); + const String custom_head_include = p_preset->get("html/head_include"); + Map<String, String> replaces; + replaces["$GODOT_URL"] = p_name + ".js"; + replaces["$GODOT_PROJECT_NAME"] = ProjectSettings::get_singleton()->get_setting("application/config/name"); + replaces["$GODOT_HEAD_INCLUDE"] = head_include + custom_head_include; + replaces["$GODOT_CONFIG"] = str_config; + _replace_strings(replaces, p_html); +} + +Error EditorExportPlatformJavaScript::_add_manifest_icon(const String &p_path, const String &p_icon, int p_size, Array &r_arr) { + const String name = p_path.get_file().get_basename(); + const String icon_name = vformat("%s.%dx%d.png", name, p_size, p_size); + const String icon_dest = p_path.get_base_dir().plus_file(icon_name); + + Ref<Image> icon; + if (!p_icon.is_empty()) { + icon.instantiate(); + const Error err = ImageLoader::load_image(p_icon, icon); + if (err != OK) { + EditorNode::get_singleton()->show_warning(TTR("Could not read file:") + "\n" + p_icon); + return err; + } + if (icon->get_width() != p_size || icon->get_height() != p_size) { + icon->resize(p_size, p_size); + } + } else { + icon = _get_project_icon(); + icon->resize(p_size, p_size); + } + const Error err = icon->save_png(icon_dest); + if (err != OK) { + EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + icon_dest); + return err; + } + Dictionary icon_dict; + icon_dict["sizes"] = vformat("%dx%d", p_size, p_size); + icon_dict["type"] = "image/png"; + icon_dict["src"] = icon_name; + r_arr.push_back(icon_dict); + return err; +} + +Error EditorExportPlatformJavaScript::_build_pwa(const Ref<EditorExportPreset> &p_preset, const String p_path, const Vector<SharedObject> &p_shared_objects) { + // Service worker + const String dir = p_path.get_base_dir(); + const String name = p_path.get_file().get_basename(); + const ExportMode mode = (ExportMode)(int)p_preset->get("variant/export_type"); + Map<String, String> replaces; + replaces["@GODOT_VERSION@"] = "1"; + replaces["@GODOT_NAME@"] = name; + replaces["@GODOT_OFFLINE_PAGE@"] = name + ".offline.html"; + Array files; + replaces["@GODOT_OPT_CACHE@"] = Variant(files).to_json_string(); + files.push_back(name + ".html"); + files.push_back(name + ".js"); + files.push_back(name + ".wasm"); + files.push_back(name + ".pck"); + files.push_back(name + ".offline.html"); + if (p_preset->get("html/export_icon")) { + files.push_back(name + ".icon.png"); + files.push_back(name + ".apple-touch-icon.png"); + } + if (mode == EXPORT_MODE_THREADS) { + files.push_back(name + ".worker.js"); + files.push_back(name + ".audio.worklet.js"); + } else if (mode == EXPORT_MODE_GDNATIVE) { + files.push_back(name + ".side.wasm"); + for (int i = 0; i < p_shared_objects.size(); i++) { + files.push_back(p_shared_objects[i].path.get_file()); + } + } + replaces["@GODOT_CACHE@"] = Variant(files).to_json_string(); + + const String sw_path = dir.plus_file(name + ".service.worker.js"); + Vector<uint8_t> sw; + { + FileAccess *f = FileAccess::open(sw_path, FileAccess::READ); + if (!f) { + EditorNode::get_singleton()->show_warning(TTR("Could not read file:") + "\n" + sw_path); + return ERR_FILE_CANT_READ; + } + sw.resize(f->get_length()); + f->get_buffer(sw.ptrw(), sw.size()); + memdelete(f); + f = nullptr; + } + _replace_strings(replaces, sw); + Error err = _write_or_error(sw.ptr(), sw.size(), dir.plus_file(name + ".service.worker.js")); + if (err != OK) { + return err; + } + + // Custom offline page + const String offline_page = p_preset->get("progressive_web_app/offline_page"); + if (!offline_page.is_empty()) { + DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + const String offline_dest = dir.plus_file(name + ".offline.html"); + err = da->copy(ProjectSettings::get_singleton()->globalize_path(offline_page), offline_dest); + if (err != OK) { + EditorNode::get_singleton()->show_warning(TTR("Could not read file:") + "\n" + offline_dest); + return err; + } + } + + // Manifest + const char *modes[4] = { "fullscreen", "standalone", "minimal-ui", "browser" }; + const char *orientations[3] = { "any", "landscape", "portrait" }; + const int display = CLAMP(int(p_preset->get("progressive_web_app/display")), 0, 4); + const int orientation = CLAMP(int(p_preset->get("progressive_web_app/orientation")), 0, 3); + + Dictionary manifest; + String proj_name = ProjectSettings::get_singleton()->get_setting("application/config/name"); + if (proj_name.is_empty()) { + proj_name = "Godot Game"; + } + manifest["name"] = proj_name; + manifest["start_url"] = "./" + name + ".html"; + manifest["display"] = String::utf8(modes[display]); + manifest["orientation"] = String::utf8(orientations[orientation]); + manifest["background_color"] = "#" + p_preset->get("progressive_web_app/background_color").operator Color().to_html(false); + + Array icons_arr; + const String icon144_path = p_preset->get("progressive_web_app/icon_144x144"); + err = _add_manifest_icon(p_path, icon144_path, 144, icons_arr); + if (err != OK) { + return err; + } + const String icon180_path = p_preset->get("progressive_web_app/icon_180x180"); + err = _add_manifest_icon(p_path, icon180_path, 180, icons_arr); + if (err != OK) { + return err; + } + const String icon512_path = p_preset->get("progressive_web_app/icon_512x512"); + err = _add_manifest_icon(p_path, icon512_path, 512, icons_arr); + if (err != OK) { + return err; + } + manifest["icons"] = icons_arr; + + CharString cs = Variant(manifest).to_json_string().utf8(); + err = _write_or_error((const uint8_t *)cs.get_data(), cs.length(), dir.plus_file(name + ".manifest.json")); + if (err != OK) { + return err; + } + + return OK; +} + +void EditorExportPlatformJavaScript::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) { + if (p_preset->get("vram_texture_compression/for_desktop")) { + r_features->push_back("s3tc"); + } + + if (p_preset->get("vram_texture_compression/for_mobile")) { + String driver = ProjectSettings::get_singleton()->get("rendering/driver/driver_name"); + if (driver == "GLES2") { + r_features->push_back("etc"); + } else if (driver == "Vulkan") { + // FIXME: Review if this is correct. + r_features->push_back("etc2"); + } + } + ExportMode mode = (ExportMode)(int)p_preset->get("variant/export_type"); + if (mode == EXPORT_MODE_THREADS) { + r_features->push_back("threads"); + } else if (mode == EXPORT_MODE_GDNATIVE) { + r_features->push_back("wasm32"); + } +} + +void EditorExportPlatformJavaScript::get_export_options(List<ExportOption> *r_options) { + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), "")); + + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "variant/export_type", PROPERTY_HINT_ENUM, "Regular,Threads,GDNative"), 0)); // Export type. + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "vram_texture_compression/for_desktop"), true)); // S3TC + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "vram_texture_compression/for_mobile"), false)); // ETC or ETC2, depending on renderer + + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "html/export_icon"), true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "html/custom_html_shell", PROPERTY_HINT_FILE, "*.html"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "html/head_include", PROPERTY_HINT_MULTILINE_TEXT), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "html/canvas_resize_policy", PROPERTY_HINT_ENUM, "None,Project,Adaptive"), 2)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "html/focus_canvas_on_start"), true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "html/experimental_virtual_keyboard"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "progressive_web_app/enabled"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "progressive_web_app/offline_page", PROPERTY_HINT_FILE, "*.html"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "progressive_web_app/display", PROPERTY_HINT_ENUM, "Fullscreen,Standalone,Minimal UI,Browser"), 1)); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "progressive_web_app/orientation", PROPERTY_HINT_ENUM, "Any,Landscape,Portrait"), 0)); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "progressive_web_app/icon_144x144", PROPERTY_HINT_FILE, "*.png,*.webp,*.svg,*.svgz"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "progressive_web_app/icon_180x180", PROPERTY_HINT_FILE, "*.png,*.webp,*.svg,*.svgz"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "progressive_web_app/icon_512x512", PROPERTY_HINT_FILE, "*.png,*.webp,*.svg,*.svgz"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::COLOR, "progressive_web_app/background_color", PROPERTY_HINT_COLOR_NO_ALPHA), Color())); +} + +String EditorExportPlatformJavaScript::get_name() const { + return "HTML5"; +} + +String EditorExportPlatformJavaScript::get_os_name() const { + return "HTML5"; +} + +Ref<Texture2D> EditorExportPlatformJavaScript::get_logo() const { + return logo; +} + +bool EditorExportPlatformJavaScript::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const { + String err; + bool valid = false; + ExportMode mode = (ExportMode)(int)p_preset->get("variant/export_type"); + + // Look for export templates (first official, and if defined custom templates). + bool dvalid = exists_export_template(_get_template_name(mode, true), &err); + bool rvalid = exists_export_template(_get_template_name(mode, false), &err); + + if (p_preset->get("custom_template/debug") != "") { + dvalid = FileAccess::exists(p_preset->get("custom_template/debug")); + if (!dvalid) { + err += TTR("Custom debug template not found.") + "\n"; + } + } + if (p_preset->get("custom_template/release") != "") { + rvalid = FileAccess::exists(p_preset->get("custom_template/release")); + if (!rvalid) { + err += TTR("Custom release template not found.") + "\n"; + } + } + + valid = dvalid || rvalid; + r_missing_templates = !valid; + + // Validate the rest of the configuration. + + if (p_preset->get("vram_texture_compression/for_mobile")) { + String etc_error = test_etc2(); + if (etc_error != String()) { + valid = false; + err += etc_error; + } + } + + if (!err.is_empty()) { + r_error = err; + } + + return valid; +} + +List<String> EditorExportPlatformJavaScript::get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const { + List<String> list; + list.push_back("html"); + return list; +} + +Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { + ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags); + + const String custom_debug = p_preset->get("custom_template/debug"); + const String custom_release = p_preset->get("custom_template/release"); + const String custom_html = p_preset->get("html/custom_html_shell"); + const bool export_icon = p_preset->get("html/export_icon"); + const bool pwa = p_preset->get("progressive_web_app/enabled"); + + const String base_dir = p_path.get_base_dir(); + const String base_path = p_path.get_basename(); + const String base_name = p_path.get_file().get_basename(); + + // Find the correct template + String template_path = p_debug ? custom_debug : custom_release; + template_path = template_path.strip_edges(); + if (template_path == String()) { + ExportMode mode = (ExportMode)(int)p_preset->get("variant/export_type"); + template_path = find_export_template(_get_template_name(mode, p_debug)); + } + + if (!DirAccess::exists(base_dir)) { + return ERR_FILE_BAD_PATH; + } + + if (template_path != String() && !FileAccess::exists(template_path)) { + EditorNode::get_singleton()->show_warning(TTR("Template file not found:") + "\n" + template_path); + return ERR_FILE_NOT_FOUND; + } + + // Export pck and shared objects + Vector<SharedObject> shared_objects; + String pck_path = base_path + ".pck"; + Error error = save_pack(p_preset, pck_path, &shared_objects); + if (error != OK) { + EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + pck_path); + return error; + } + DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + for (int i = 0; i < shared_objects.size(); i++) { + String dst = base_dir.plus_file(shared_objects[i].path.get_file()); + error = da->copy(shared_objects[i].path, dst); + if (error != OK) { + EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + shared_objects[i].path.get_file()); + memdelete(da); + return error; + } + } + memdelete(da); + da = nullptr; + + // Extract templates. + error = _extract_template(template_path, base_dir, base_name, pwa); + if (error) { + return error; + } + + // Parse generated file sizes (pck and wasm, to help show a meaningful loading bar). + Dictionary file_sizes; + FileAccess *f = nullptr; + f = FileAccess::open(pck_path, FileAccess::READ); + if (f) { + file_sizes[pck_path.get_file()] = (uint64_t)f->get_length(); + memdelete(f); + f = nullptr; + } + f = FileAccess::open(base_path + ".wasm", FileAccess::READ); + if (f) { + file_sizes[base_name + ".wasm"] = (uint64_t)f->get_length(); + memdelete(f); + f = nullptr; + } + + // Read the HTML shell file (custom or from template). + const String html_path = custom_html.is_empty() ? base_path + ".html" : custom_html; + Vector<uint8_t> html; + f = FileAccess::open(html_path, FileAccess::READ); + if (!f) { + EditorNode::get_singleton()->show_warning(TTR("Could not read HTML shell:") + "\n" + html_path); + return ERR_FILE_CANT_READ; + } + html.resize(f->get_length()); + f->get_buffer(html.ptrw(), html.size()); + memdelete(f); + f = nullptr; + + // Generate HTML file with replaced strings. + _fix_html(html, p_preset, base_name, p_debug, p_flags, shared_objects, file_sizes); + Error err = _write_or_error(html.ptr(), html.size(), p_path); + if (err != OK) { + return err; + } + html.resize(0); + + // Export splash (why?) + Ref<Image> splash = _get_project_splash(); + const String splash_png_path = base_path + ".png"; + if (splash->save_png(splash_png_path) != OK) { + EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + splash_png_path); + return ERR_FILE_CANT_WRITE; + } + + // Save a favicon that can be accessed without waiting for the project to finish loading. + // This way, the favicon can be displayed immediately when loading the page. + if (export_icon) { + Ref<Image> favicon = _get_project_icon(); + const String favicon_png_path = base_path + ".icon.png"; + if (favicon->save_png(favicon_png_path) != OK) { + EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + favicon_png_path); + return ERR_FILE_CANT_WRITE; + } + favicon->resize(180, 180); + const String apple_icon_png_path = base_path + ".apple-touch-icon.png"; + if (favicon->save_png(apple_icon_png_path) != OK) { + EditorNode::get_singleton()->show_warning(TTR("Could not write file:") + "\n" + apple_icon_png_path); + return ERR_FILE_CANT_WRITE; + } + } + + // Generate the PWA worker and manifest + if (pwa) { + err = _build_pwa(p_preset, p_path, shared_objects); + if (err != OK) { + return err; + } + } + + return OK; +} + +bool EditorExportPlatformJavaScript::poll_export() { + Ref<EditorExportPreset> preset; + + for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) { + Ref<EditorExportPreset> ep = EditorExport::get_singleton()->get_export_preset(i); + if (ep->is_runnable() && ep->get_platform() == this) { + preset = ep; + break; + } + } + + int prev = menu_options; + menu_options = preset.is_valid(); + if (server->is_listening()) { + if (menu_options == 0) { + MutexLock lock(server_lock); + server->stop(); + } else { + menu_options += 1; + } + } + return menu_options != prev; +} + +Ref<ImageTexture> EditorExportPlatformJavaScript::get_option_icon(int p_index) const { + return p_index == 1 ? stop_icon : EditorExportPlatform::get_option_icon(p_index); +} + +int EditorExportPlatformJavaScript::get_options_count() const { + return menu_options; +} + +Error EditorExportPlatformJavaScript::run(const Ref<EditorExportPreset> &p_preset, int p_option, int p_debug_flags) { + if (p_option == 1) { + MutexLock lock(server_lock); + server->stop(); + return OK; + } + + const String dest = EditorPaths::get_singleton()->get_cache_dir().plus_file("web"); + DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + if (!da->dir_exists(dest)) { + Error err = da->make_dir_recursive(dest); + if (err != OK) { + EditorNode::get_singleton()->show_warning(TTR("Could not create HTTP server directory:") + "\n" + dest); + return err; + } + } + const String basepath = dest.plus_file("tmp_js_export"); + Error err = export_project(p_preset, true, basepath + ".html", p_debug_flags); + if (err != OK) { + // Export generates several files, clean them up on failure. + DirAccess::remove_file_or_error(basepath + ".html"); + DirAccess::remove_file_or_error(basepath + ".offline.html"); + DirAccess::remove_file_or_error(basepath + ".js"); + DirAccess::remove_file_or_error(basepath + ".worker.js"); + DirAccess::remove_file_or_error(basepath + ".audio.worklet.js"); + DirAccess::remove_file_or_error(basepath + ".service.worker.js"); + DirAccess::remove_file_or_error(basepath + ".pck"); + DirAccess::remove_file_or_error(basepath + ".png"); + DirAccess::remove_file_or_error(basepath + ".side.wasm"); + DirAccess::remove_file_or_error(basepath + ".wasm"); + DirAccess::remove_file_or_error(basepath + ".icon.png"); + DirAccess::remove_file_or_error(basepath + ".apple-touch-icon.png"); + return err; + } + + const uint16_t bind_port = EDITOR_GET("export/web/http_port"); + // Resolve host if needed. + const String bind_host = EDITOR_GET("export/web/http_host"); + IPAddress bind_ip; + if (bind_host.is_valid_ip_address()) { + bind_ip = bind_host; + } else { + bind_ip = IP::get_singleton()->resolve_hostname(bind_host); + } + ERR_FAIL_COND_V_MSG(!bind_ip.is_valid(), ERR_INVALID_PARAMETER, "Invalid editor setting 'export/web/http_host': '" + bind_host + "'. Try using '127.0.0.1'."); + + const bool use_ssl = EDITOR_GET("export/web/use_ssl"); + const String ssl_key = EDITOR_GET("export/web/ssl_key"); + const String ssl_cert = EDITOR_GET("export/web/ssl_certificate"); + + // Restart server. + { + MutexLock lock(server_lock); + + server->stop(); + err = server->listen(bind_port, bind_ip, use_ssl, ssl_key, ssl_cert); + } + if (err != OK) { + EditorNode::get_singleton()->show_warning(TTR("Error starting HTTP server:") + "\n" + itos(err)); + return err; + } + + OS::get_singleton()->shell_open(String((use_ssl ? "https://" : "http://") + bind_host + ":" + itos(bind_port) + "/tmp_js_export.html")); + // FIXME: Find out how to clean up export files after running the successfully + // exported game. Might not be trivial. + return OK; +} + +Ref<Texture2D> EditorExportPlatformJavaScript::get_run_icon() const { + return run_icon; +} + +void EditorExportPlatformJavaScript::_server_thread_poll(void *data) { + EditorExportPlatformJavaScript *ej = (EditorExportPlatformJavaScript *)data; + while (!ej->server_quit) { + OS::get_singleton()->delay_usec(1000); + { + MutexLock lock(ej->server_lock); + ej->server->poll(); + } + } +} + +EditorExportPlatformJavaScript::EditorExportPlatformJavaScript() { + server.instantiate(); + server_thread.start(_server_thread_poll, this); + + Ref<Image> img = memnew(Image(_javascript_logo)); + logo.instantiate(); + logo->create_from_image(img); + + img = Ref<Image>(memnew(Image(_javascript_run_icon))); + run_icon.instantiate(); + run_icon->create_from_image(img); + + Ref<Theme> theme = EditorNode::get_singleton()->get_editor_theme(); + if (theme.is_valid()) { + stop_icon = theme->get_icon("Stop", "EditorIcons"); + } else { + stop_icon.instantiate(); + } +} + +EditorExportPlatformJavaScript::~EditorExportPlatformJavaScript() { + server->stop(); + server_quit = true; + server_thread.wait_to_finish(); +} diff --git a/platform/javascript/export/export_plugin.h b/platform/javascript/export/export_plugin.h new file mode 100644 index 0000000000..736edfe3a8 --- /dev/null +++ b/platform/javascript/export/export_plugin.h @@ -0,0 +1,149 @@ +/*************************************************************************/ +/* export_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef JAVASCRIPT_EXPORT_PLUGIN_H +#define JAVASCRIPT_EXPORT_PLUGIN_H + +#include "core/io/image_loader.h" +#include "core/io/stream_peer_ssl.h" +#include "core/io/tcp_server.h" +#include "core/io/zip_io.h" +#include "editor/editor_export.h" +#include "editor/editor_node.h" +#include "main/splash.gen.h" +#include "platform/javascript/logo.gen.h" +#include "platform/javascript/run_icon.gen.h" + +#include "export_server.h" + +class EditorExportPlatformJavaScript : public EditorExportPlatform { + GDCLASS(EditorExportPlatformJavaScript, EditorExportPlatform); + + Ref<ImageTexture> logo; + Ref<ImageTexture> run_icon; + Ref<ImageTexture> stop_icon; + int menu_options = 0; + + Ref<EditorHTTPServer> server; + bool server_quit = false; + Mutex server_lock; + Thread server_thread; + + enum ExportMode { + EXPORT_MODE_NORMAL = 0, + EXPORT_MODE_THREADS = 1, + EXPORT_MODE_GDNATIVE = 2, + }; + + String _get_template_name(ExportMode p_mode, bool p_debug) const { + String name = "webassembly"; + switch (p_mode) { + case EXPORT_MODE_THREADS: + name += "_threads"; + break; + case EXPORT_MODE_GDNATIVE: + name += "_gdnative"; + break; + default: + break; + } + if (p_debug) { + name += "_debug.zip"; + } else { + name += "_release.zip"; + } + return name; + } + + Ref<Image> _get_project_icon() const { + Ref<Image> icon; + icon.instantiate(); + const String icon_path = String(GLOBAL_GET("application/config/icon")).strip_edges(); + if (icon_path.is_empty() || ImageLoader::load_image(icon_path, icon) != OK) { + return EditorNode::get_singleton()->get_editor_theme()->get_icon("DefaultProjectIcon", "EditorIcons")->get_image(); + } + return icon; + } + + Ref<Image> _get_project_splash() const { + Ref<Image> splash; + splash.instantiate(); + const String splash_path = String(GLOBAL_GET("application/boot_splash/image")).strip_edges(); + if (splash_path.is_empty() || ImageLoader::load_image(splash_path, splash) != OK) { + return Ref<Image>(memnew(Image(boot_splash_png))); + } + return splash; + } + + Error _extract_template(const String &p_template, const String &p_dir, const String &p_name, bool pwa); + void _replace_strings(Map<String, String> p_replaces, Vector<uint8_t> &r_template); + void _fix_html(Vector<uint8_t> &p_html, const Ref<EditorExportPreset> &p_preset, const String &p_name, bool p_debug, int p_flags, const Vector<SharedObject> p_shared_objects, const Dictionary &p_file_sizes); + Error _add_manifest_icon(const String &p_path, const String &p_icon, int p_size, Array &r_arr); + Error _build_pwa(const Ref<EditorExportPreset> &p_preset, const String p_path, const Vector<SharedObject> &p_shared_objects); + Error _write_or_error(const uint8_t *p_content, int p_len, String p_path); + + static void _server_thread_poll(void *data); + +public: + virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) override; + + virtual void get_export_options(List<ExportOption> *r_options) override; + + virtual String get_name() const override; + virtual String get_os_name() const override; + virtual Ref<Texture2D> get_logo() const override; + + virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override; + virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override; + virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override; + + virtual bool poll_export() override; + virtual int get_options_count() const override; + virtual String get_option_label(int p_index) const override { return p_index ? TTR("Stop HTTP Server") : TTR("Run in Browser"); } + virtual String get_option_tooltip(int p_index) const override { return p_index ? TTR("Stop HTTP Server") : TTR("Run exported HTML in the system's default browser."); } + virtual Ref<ImageTexture> get_option_icon(int p_index) const override; + virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_option, int p_debug_flags) override; + virtual Ref<Texture2D> get_run_icon() const override; + + virtual void get_platform_features(List<String> *r_features) override { + r_features->push_back("web"); + r_features->push_back(get_os_name()); + } + + virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) override { + } + + String get_debug_protocol() const override { return "ws://"; } + + EditorExportPlatformJavaScript(); + ~EditorExportPlatformJavaScript(); +}; + +#endif diff --git a/platform/javascript/export/export_server.h b/platform/javascript/export/export_server.h new file mode 100644 index 0000000000..87cbdcab47 --- /dev/null +++ b/platform/javascript/export/export_server.h @@ -0,0 +1,254 @@ +/*************************************************************************/ +/* export_server.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef JAVASCRIPT_EXPORT_SERVER_H +#define JAVASCRIPT_EXPORT_SERVER_H + +#include "core/io/image_loader.h" +#include "core/io/stream_peer_ssl.h" +#include "core/io/tcp_server.h" +#include "core/io/zip_io.h" +#include "editor/editor_export.h" +#include "editor/editor_node.h" + +class EditorHTTPServer : public RefCounted { +private: + Ref<TCPServer> server; + Map<String, String> mimes; + Ref<StreamPeerTCP> tcp; + Ref<StreamPeerSSL> ssl; + Ref<StreamPeer> peer; + Ref<CryptoKey> key; + Ref<X509Certificate> cert; + bool use_ssl = false; + uint64_t time = 0; + uint8_t req_buf[4096]; + int req_pos = 0; + + void _clear_client() { + peer = Ref<StreamPeer>(); + ssl = Ref<StreamPeerSSL>(); + tcp = Ref<StreamPeerTCP>(); + memset(req_buf, 0, sizeof(req_buf)); + time = 0; + req_pos = 0; + } + + void _set_internal_certs(Ref<Crypto> p_crypto) { + const String cache_path = EditorPaths::get_singleton()->get_cache_dir(); + const String key_path = cache_path.plus_file("html5_server.key"); + const String crt_path = cache_path.plus_file("html5_server.crt"); + bool regen = !FileAccess::exists(key_path) || !FileAccess::exists(crt_path); + if (!regen) { + key = Ref<CryptoKey>(CryptoKey::create()); + cert = Ref<X509Certificate>(X509Certificate::create()); + if (key->load(key_path) != OK || cert->load(crt_path) != OK) { + regen = true; + } + } + if (regen) { + key = p_crypto->generate_rsa(2048); + key->save(key_path); + cert = p_crypto->generate_self_signed_certificate(key, "CN=godot-debug.local,O=A Game Dev,C=XXA", "20140101000000", "20340101000000"); + cert->save(crt_path); + } + } + +public: + EditorHTTPServer() { + mimes["html"] = "text/html"; + mimes["js"] = "application/javascript"; + mimes["json"] = "application/json"; + mimes["pck"] = "application/octet-stream"; + mimes["png"] = "image/png"; + mimes["svg"] = "image/svg"; + mimes["wasm"] = "application/wasm"; + server.instantiate(); + stop(); + } + + void stop() { + server->stop(); + _clear_client(); + } + + Error listen(int p_port, IPAddress p_address, bool p_use_ssl, String p_ssl_key, String p_ssl_cert) { + use_ssl = p_use_ssl; + if (use_ssl) { + Ref<Crypto> crypto = Crypto::create(); + if (crypto.is_null()) { + return ERR_UNAVAILABLE; + } + if (!p_ssl_key.is_empty() && !p_ssl_cert.is_empty()) { + key = Ref<CryptoKey>(CryptoKey::create()); + Error err = key->load(p_ssl_key); + ERR_FAIL_COND_V(err != OK, err); + cert = Ref<X509Certificate>(X509Certificate::create()); + err = cert->load(p_ssl_cert); + ERR_FAIL_COND_V(err != OK, err); + } else { + _set_internal_certs(crypto); + } + } + return server->listen(p_port, p_address); + } + + bool is_listening() const { + return server->is_listening(); + } + + void _send_response() { + Vector<String> psa = String((char *)req_buf).split("\r\n"); + int len = psa.size(); + ERR_FAIL_COND_MSG(len < 4, "Not enough response headers, got: " + itos(len) + ", expected >= 4."); + + Vector<String> req = psa[0].split(" ", false); + ERR_FAIL_COND_MSG(req.size() < 2, "Invalid protocol or status code."); + + // Wrong protocol + ERR_FAIL_COND_MSG(req[0] != "GET" || req[2] != "HTTP/1.1", "Invalid method or HTTP version."); + + const int query_index = req[1].find_char('?'); + const String path = (query_index == -1) ? req[1] : req[1].substr(0, query_index); + + const String req_file = path.get_file(); + const String req_ext = path.get_extension(); + const String cache_path = EditorPaths::get_singleton()->get_cache_dir().plus_file("web"); + const String filepath = cache_path.plus_file(req_file); + + if (!mimes.has(req_ext) || !FileAccess::exists(filepath)) { + String s = "HTTP/1.1 404 Not Found\r\n"; + s += "Connection: Close\r\n"; + s += "\r\n"; + CharString cs = s.utf8(); + peer->put_data((const uint8_t *)cs.get_data(), cs.size() - 1); + return; + } + const String ctype = mimes[req_ext]; + + FileAccess *f = FileAccess::open(filepath, FileAccess::READ); + ERR_FAIL_COND(!f); + String s = "HTTP/1.1 200 OK\r\n"; + s += "Connection: Close\r\n"; + s += "Content-Type: " + ctype + "\r\n"; + s += "Access-Control-Allow-Origin: *\r\n"; + s += "Cross-Origin-Opener-Policy: same-origin\r\n"; + s += "Cross-Origin-Embedder-Policy: require-corp\r\n"; + s += "Cache-Control: no-store, max-age=0\r\n"; + s += "\r\n"; + CharString cs = s.utf8(); + Error err = peer->put_data((const uint8_t *)cs.get_data(), cs.size() - 1); + if (err != OK) { + memdelete(f); + ERR_FAIL(); + } + + while (true) { + uint8_t bytes[4096]; + uint64_t read = f->get_buffer(bytes, 4096); + if (read == 0) { + break; + } + err = peer->put_data(bytes, read); + if (err != OK) { + memdelete(f); + ERR_FAIL(); + } + } + memdelete(f); + } + + void poll() { + if (!server->is_listening()) { + return; + } + if (tcp.is_null()) { + if (!server->is_connection_available()) { + return; + } + tcp = server->take_connection(); + peer = tcp; + time = OS::get_singleton()->get_ticks_usec(); + } + if (OS::get_singleton()->get_ticks_usec() - time > 1000000) { + _clear_client(); + return; + } + if (tcp->get_status() != StreamPeerTCP::STATUS_CONNECTED) { + return; + } + + if (use_ssl) { + if (ssl.is_null()) { + ssl = Ref<StreamPeerSSL>(StreamPeerSSL::create()); + peer = ssl; + ssl->set_blocking_handshake_enabled(false); + if (ssl->accept_stream(tcp, key, cert) != OK) { + _clear_client(); + return; + } + } + ssl->poll(); + if (ssl->get_status() == StreamPeerSSL::STATUS_HANDSHAKING) { + // Still handshaking, keep waiting. + return; + } + if (ssl->get_status() != StreamPeerSSL::STATUS_CONNECTED) { + _clear_client(); + return; + } + } + + while (true) { + char *r = (char *)req_buf; + int l = req_pos - 1; + if (l > 3 && r[l] == '\n' && r[l - 1] == '\r' && r[l - 2] == '\n' && r[l - 3] == '\r') { + _send_response(); + _clear_client(); + return; + } + + int read = 0; + ERR_FAIL_COND(req_pos >= 4096); + Error err = peer->get_partial_data(&req_buf[req_pos], 1, read); + if (err != OK) { + // Got an error + _clear_client(); + return; + } else if (read != 1) { + // Busy, wait next poll + return; + } + req_pos += read; + } + } +}; + +#endif diff --git a/platform/osx/export/export.cpp b/platform/osx/export/export.cpp index 131c4b821e..1164d76580 100644 --- a/platform/osx/export/export.cpp +++ b/platform/osx/export/export.cpp @@ -30,1153 +30,7 @@ #include "export.h" -#include "core/config/project_settings.h" -#include "core/io/dir_access.h" -#include "core/io/file_access.h" -#include "core/io/marshalls.h" -#include "core/io/resource_saver.h" -#include "core/io/zip_io.h" -#include "core/os/os.h" -#include "core/version.h" -#include "editor/editor_export.h" -#include "editor/editor_node.h" -#include "editor/editor_settings.h" -#include "platform/osx/logo.gen.h" - -#include <sys/stat.h> - -class EditorExportPlatformOSX : public EditorExportPlatform { - GDCLASS(EditorExportPlatformOSX, EditorExportPlatform); - - int version_code = 0; - - Ref<ImageTexture> logo; - - void _fix_plist(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &plist, const String &p_binary); - void _make_icon(const Ref<Image> &p_icon, Vector<uint8_t> &p_data); - - Error _notarize(const Ref<EditorExportPreset> &p_preset, const String &p_path); - Error _code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path, const String &p_ent_path); - Error _create_dmg(const String &p_dmg_path, const String &p_pkg_name, const String &p_app_path_name); - void _zip_folder_recursive(zipFile &p_zip, const String &p_root_path, const String &p_folder, const String &p_pkg_name); - -#ifdef OSX_ENABLED - bool use_codesign() const { return true; } - bool use_dmg() const { return true; } -#else - bool use_codesign() const { return false; } - bool use_dmg() const { return false; } -#endif - bool is_package_name_valid(const String &p_package, String *r_error = nullptr) const { - String pname = p_package; - - if (pname.length() == 0) { - if (r_error) { - *r_error = TTR("Identifier is missing."); - } - return false; - } - - for (int i = 0; i < pname.length(); i++) { - char32_t c = pname[i]; - if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-' || c == '.')) { - if (r_error) { - *r_error = vformat(TTR("The character '%s' is not allowed in Identifier."), String::chr(c)); - } - return false; - } - } - - return true; - } - -protected: - virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) override; - virtual void get_export_options(List<ExportOption> *r_options) override; - -public: - virtual String get_name() const override { return "macOS"; } - virtual String get_os_name() const override { return "macOS"; } - virtual Ref<Texture2D> get_logo() const override { return logo; } - - virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override { - List<String> list; - if (use_dmg()) { - list.push_back("dmg"); - } - list.push_back("zip"); - return list; - } - virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override; - - virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override; - - virtual void get_platform_features(List<String> *r_features) override { - r_features->push_back("pc"); - r_features->push_back("s3tc"); - r_features->push_back("macOS"); - } - - virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) override { - } - - EditorExportPlatformOSX(); - ~EditorExportPlatformOSX(); -}; - -void EditorExportPlatformOSX::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) { - if (p_preset->get("texture_format/s3tc")) { - r_features->push_back("s3tc"); - } - if (p_preset->get("texture_format/etc")) { - r_features->push_back("etc"); - } - if (p_preset->get("texture_format/etc2")) { - r_features->push_back("etc2"); - } - - r_features->push_back("64"); -} - -void EditorExportPlatformOSX::get_export_options(List<ExportOption> *r_options) { - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), "")); - - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/info"), "Made with Godot Engine")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_FILE, "*.png,*.icns"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/bundle_identifier", PROPERTY_HINT_PLACEHOLDER_TEXT, "com.example.game"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/signature"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/app_category", PROPERTY_HINT_ENUM, "Business,Developer-tools,Education,Entertainment,Finance,Games,Action-games,Adventure-games,Arcade-games,Board-games,Card-games,Casino-games,Dice-games,Educational-games,Family-games,Kids-games,Music-games,Puzzle-games,Racing-games,Role-playing-games,Simulation-games,Sports-games,Strategy-games,Trivia-games,Word-games,Graphics-design,Healthcare-fitness,Lifestyle,Medical,Music,News,Photography,Productivity,Reference,Social-networking,Sports,Travel,Utilities,Video,Weather"), "Games")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/short_version"), "1.0")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/version"), "1.0")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/copyright"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "display/high_res"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/camera_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the camera"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/microphone_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the microphone"), "")); - -#ifdef OSX_ENABLED - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/enable"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/identity", PROPERTY_HINT_PLACEHOLDER_TEXT, "Type: Name (ID)"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/timestamp"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/hardened_runtime"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/replace_existing_signature"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/entitlements/custom_file", PROPERTY_HINT_GLOBAL_FILE, "*.plist"), "")); - - if (!Engine::get_singleton()->has_singleton("GodotSharp")) { - // These entitlements are required to run managed code, and are always enabled in Mono builds. - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/allow_jit_code_execution"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/allow_unsigned_executable_memory"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/allow_dyld_environment_variables"), false)); - } - - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/disable_library_validation"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/audio_input"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/camera"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/location"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/address_book"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/calendars"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/photos_library"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/apple_events"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/debugging"), false)); - - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/enabled"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/network_server"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/network_client"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/device_usb"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/device_bluetooth"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_downloads", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0)); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_pictures", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0)); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_music", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0)); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_movies", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0)); - - r_options->push_back(ExportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "codesign/custom_options"), PackedStringArray())); - - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "notarization/enable"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_id_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Apple ID email"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_id_password", PROPERTY_HINT_PLACEHOLDER_TEXT, "Enable two-factor authentication and provide app-specific password"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_team_id", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide team ID if your Apple ID belongs to multiple teams"), "")); -#endif - - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/s3tc"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc2"), false)); -} - -void _rgba8_to_packbits_encode(int p_ch, int p_size, Vector<uint8_t> &p_source, Vector<uint8_t> &p_dest) { - int src_len = p_size * p_size; - - Vector<uint8_t> result; - result.resize(src_len * 1.25); //temp vector for rle encoded data, make it 25% larger for worst case scenario - int res_size = 0; - - uint8_t buf[128]; - int buf_size = 0; - - int i = 0; - while (i < src_len) { - uint8_t cur = p_source.ptr()[i * 4 + p_ch]; - - if (i < src_len - 2) { - if ((p_source.ptr()[(i + 1) * 4 + p_ch] == cur) && (p_source.ptr()[(i + 2) * 4 + p_ch] == cur)) { - if (buf_size > 0) { - result.write[res_size++] = (uint8_t)(buf_size - 1); - memcpy(&result.write[res_size], &buf, buf_size); - res_size += buf_size; - buf_size = 0; - } - - uint8_t lim = i + 130 >= src_len ? src_len - i - 1 : 130; - bool hit_lim = true; - - for (int j = 3; j <= lim; j++) { - if (p_source.ptr()[(i + j) * 4 + p_ch] != cur) { - hit_lim = false; - i = i + j - 1; - result.write[res_size++] = (uint8_t)(j - 3 + 0x80); - result.write[res_size++] = cur; - break; - } - } - if (hit_lim) { - result.write[res_size++] = (uint8_t)(lim - 3 + 0x80); - result.write[res_size++] = cur; - i = i + lim; - } - } else { - buf[buf_size++] = cur; - if (buf_size == 128) { - result.write[res_size++] = (uint8_t)(buf_size - 1); - memcpy(&result.write[res_size], &buf, buf_size); - res_size += buf_size; - buf_size = 0; - } - } - } else { - buf[buf_size++] = cur; - result.write[res_size++] = (uint8_t)(buf_size - 1); - memcpy(&result.write[res_size], &buf, buf_size); - res_size += buf_size; - buf_size = 0; - } - - i++; - } - - int ofs = p_dest.size(); - p_dest.resize(p_dest.size() + res_size); - memcpy(&p_dest.write[ofs], result.ptr(), res_size); -} - -void EditorExportPlatformOSX::_make_icon(const Ref<Image> &p_icon, Vector<uint8_t> &p_data) { - Ref<ImageTexture> it = memnew(ImageTexture); - - Vector<uint8_t> data; - - data.resize(8); - data.write[0] = 'i'; - data.write[1] = 'c'; - data.write[2] = 'n'; - data.write[3] = 's'; - - struct MacOSIconInfo { - const char *name; - const char *mask_name; - bool is_png; - int size; - }; - - static const MacOSIconInfo icon_infos[] = { - { "ic10", "", true, 1024 }, //1024x1024 32-bit PNG and 512x512@2x 32-bit "retina" PNG - { "ic09", "", true, 512 }, //512×512 32-bit PNG - { "ic14", "", true, 512 }, //256x256@2x 32-bit "retina" PNG - { "ic08", "", true, 256 }, //256×256 32-bit PNG - { "ic13", "", true, 256 }, //128x128@2x 32-bit "retina" PNG - { "ic07", "", true, 128 }, //128x128 32-bit PNG - { "ic12", "", true, 64 }, //32x32@2x 32-bit "retina" PNG - { "ic11", "", true, 32 }, //16x16@2x 32-bit "retina" PNG - { "il32", "l8mk", false, 32 }, //32x32 24-bit RLE + 8-bit uncompressed mask - { "is32", "s8mk", false, 16 } //16x16 24-bit RLE + 8-bit uncompressed mask - }; - - for (uint64_t i = 0; i < (sizeof(icon_infos) / sizeof(icon_infos[0])); ++i) { - Ref<Image> copy = p_icon; // does this make sense? doesn't this just increase the reference count instead of making a copy? Do we even need a copy? - copy->convert(Image::FORMAT_RGBA8); - copy->resize(icon_infos[i].size, icon_infos[i].size); - - if (icon_infos[i].is_png) { - // Encode PNG icon. - it->create_from_image(copy); - String path = EditorPaths::get_singleton()->get_cache_dir().plus_file("icon.png"); - ResourceSaver::save(path, it); - - FileAccess *f = FileAccess::open(path, FileAccess::READ); - if (!f) { - // Clean up generated file. - DirAccess::remove_file_or_error(path); - ERR_FAIL(); - } - - int ofs = data.size(); - uint64_t len = f->get_length(); - data.resize(data.size() + len + 8); - f->get_buffer(&data.write[ofs + 8], len); - memdelete(f); - len += 8; - len = BSWAP32(len); - memcpy(&data.write[ofs], icon_infos[i].name, 4); - encode_uint32(len, &data.write[ofs + 4]); - - // Clean up generated file. - DirAccess::remove_file_or_error(path); - - } else { - Vector<uint8_t> src_data = copy->get_data(); - - //encode 24bit RGB RLE icon - { - int ofs = data.size(); - data.resize(data.size() + 8); - - _rgba8_to_packbits_encode(0, icon_infos[i].size, src_data, data); // encode R - _rgba8_to_packbits_encode(1, icon_infos[i].size, src_data, data); // encode G - _rgba8_to_packbits_encode(2, icon_infos[i].size, src_data, data); // encode B - - int len = data.size() - ofs; - len = BSWAP32(len); - memcpy(&data.write[ofs], icon_infos[i].name, 4); - encode_uint32(len, &data.write[ofs + 4]); - } - - //encode 8bit mask uncompressed icon - { - int ofs = data.size(); - int len = copy->get_width() * copy->get_height(); - data.resize(data.size() + len + 8); - - for (int j = 0; j < len; j++) { - data.write[ofs + 8 + j] = src_data.ptr()[j * 4 + 3]; - } - len += 8; - len = BSWAP32(len); - memcpy(&data.write[ofs], icon_infos[i].mask_name, 4); - encode_uint32(len, &data.write[ofs + 4]); - } - } - } - - uint32_t total_len = data.size(); - total_len = BSWAP32(total_len); - encode_uint32(total_len, &data.write[4]); - - p_data = data; -} - -void EditorExportPlatformOSX::_fix_plist(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &plist, const String &p_binary) { - String str; - String strnew; - str.parse_utf8((const char *)plist.ptr(), plist.size()); - Vector<String> lines = str.split("\n"); - for (int i = 0; i < lines.size(); i++) { - if (lines[i].find("$binary") != -1) { - strnew += lines[i].replace("$binary", p_binary) + "\n"; - } else if (lines[i].find("$name") != -1) { - strnew += lines[i].replace("$name", p_binary) + "\n"; - } else if (lines[i].find("$info") != -1) { - strnew += lines[i].replace("$info", p_preset->get("application/info")) + "\n"; - } else if (lines[i].find("$bundle_identifier") != -1) { - strnew += lines[i].replace("$bundle_identifier", p_preset->get("application/bundle_identifier")) + "\n"; - } else if (lines[i].find("$short_version") != -1) { - strnew += lines[i].replace("$short_version", p_preset->get("application/short_version")) + "\n"; - } else if (lines[i].find("$version") != -1) { - strnew += lines[i].replace("$version", p_preset->get("application/version")) + "\n"; - } else if (lines[i].find("$signature") != -1) { - strnew += lines[i].replace("$signature", p_preset->get("application/signature")) + "\n"; - } else if (lines[i].find("$app_category") != -1) { - String cat = p_preset->get("application/app_category"); - strnew += lines[i].replace("$app_category", cat.to_lower()) + "\n"; - } else if (lines[i].find("$copyright") != -1) { - strnew += lines[i].replace("$copyright", p_preset->get("application/copyright")) + "\n"; - } else if (lines[i].find("$highres") != -1) { - strnew += lines[i].replace("$highres", p_preset->get("display/high_res") ? "<true/>" : "<false/>") + "\n"; - } else if (lines[i].find("$camera_usage_description") != -1) { - String description = p_preset->get("privacy/camera_usage_description"); - strnew += lines[i].replace("$camera_usage_description", description) + "\n"; - } else if (lines[i].find("$microphone_usage_description") != -1) { - String description = p_preset->get("privacy/microphone_usage_description"); - strnew += lines[i].replace("$microphone_usage_description", description) + "\n"; - } else { - strnew += lines[i] + "\n"; - } - } - - CharString cs = strnew.utf8(); - plist.resize(cs.size() - 1); - for (int i = 0; i < cs.size() - 1; i++) { - plist.write[i] = cs[i]; - } -} - -/** - If we're running the OSX version of the Godot editor we'll: - - export our application bundle to a temporary folder - - attempt to code sign it - - and then wrap it up in a DMG -**/ - -Error EditorExportPlatformOSX::_notarize(const Ref<EditorExportPreset> &p_preset, const String &p_path) { -#ifdef OSX_ENABLED - List<String> args; - - args.push_back("altool"); - args.push_back("--notarize-app"); - - args.push_back("--primary-bundle-id"); - args.push_back(p_preset->get("application/bundle_identifier")); - - args.push_back("--username"); - args.push_back(p_preset->get("notarization/apple_id_name")); - - args.push_back("--password"); - args.push_back(p_preset->get("notarization/apple_id_password")); - - args.push_back("--type"); - args.push_back("osx"); - - if (p_preset->get("notarization/apple_team_id")) { - args.push_back("--asc-provider"); - args.push_back(p_preset->get("notarization/apple_team_id")); - } - - args.push_back("--file"); - args.push_back(p_path); - - String str; - Error err = OS::get_singleton()->execute("xcrun", args, &str, nullptr, true); - ERR_FAIL_COND_V(err != OK, err); - - print_line("altool (" + p_path + "):\n" + str); - if (str.find("RequestUUID") == -1) { - EditorNode::add_io_error("altool: " + str); - return FAILED; - } else { - print_line("Note: The notarization process generally takes less than an hour. When the process is completed, you'll receive an email."); - print_line(" You can check progress manually by opening a Terminal and running the following command:"); - print_line(" \"xcrun altool --notarization-history 0 -u <your email> -p <app-specific pwd>\""); - } - -#endif - - return OK; -} - -Error EditorExportPlatformOSX::_code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path, const String &p_ent_path) { -#ifdef OSX_ENABLED - List<String> args; - - if (p_preset->get("codesign/timestamp")) { - args.push_back("--timestamp"); - } - if (p_preset->get("codesign/hardened_runtime")) { - args.push_back("--options"); - args.push_back("runtime"); - } - - if (p_path.get_extension() != "dmg") { - args.push_back("--entitlements"); - args.push_back(p_ent_path); - } - - PackedStringArray user_args = p_preset->get("codesign/custom_options"); - for (int i = 0; i < user_args.size(); i++) { - String user_arg = user_args[i].strip_edges(); - if (!user_arg.is_empty()) { - args.push_back(user_arg); - } - } - - args.push_back("-s"); - if (p_preset->get("codesign/identity") == "") { - args.push_back("-"); - } else { - args.push_back(p_preset->get("codesign/identity")); - } - - args.push_back("-v"); /* provide some more feedback */ - - if (p_preset->get("codesign/replace_existing_signature")) { - args.push_back("-f"); - } - - args.push_back(p_path); - - String str; - Error err = OS::get_singleton()->execute("codesign", args, &str, nullptr, true); - ERR_FAIL_COND_V(err != OK, err); - - print_line("codesign (" + p_path + "):\n" + str); - if (str.find("no identity found") != -1) { - EditorNode::add_io_error("codesign: no identity found"); - return FAILED; - } - if ((str.find("unrecognized blob type") != -1) || (str.find("cannot read entitlement data") != -1)) { - EditorNode::add_io_error("codesign: invalid entitlements file"); - return FAILED; - } -#endif - - return OK; -} - -Error EditorExportPlatformOSX::_create_dmg(const String &p_dmg_path, const String &p_pkg_name, const String &p_app_path_name) { - List<String> args; - - if (FileAccess::exists(p_dmg_path)) { - OS::get_singleton()->move_to_trash(p_dmg_path); - } - - args.push_back("create"); - args.push_back(p_dmg_path); - args.push_back("-volname"); - args.push_back(p_pkg_name); - args.push_back("-fs"); - args.push_back("HFS+"); - args.push_back("-srcfolder"); - args.push_back(p_app_path_name); - - String str; - Error err = OS::get_singleton()->execute("hdiutil", args, &str, nullptr, true); - ERR_FAIL_COND_V(err != OK, err); - - print_line("hdiutil returned: " + str); - if (str.find("create failed") != -1) { - if (str.find("File exists") != -1) { - EditorNode::add_io_error("hdiutil: create failed - file exists"); - } else { - EditorNode::add_io_error("hdiutil: create failed"); - } - return FAILED; - } - - return OK; -} - -Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { - ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags); - - String src_pkg_name; - - EditorProgress ep("export", "Exporting for OSX", 3, true); - - if (p_debug) { - src_pkg_name = p_preset->get("custom_template/debug"); - } else { - src_pkg_name = p_preset->get("custom_template/release"); - } - - if (src_pkg_name == "") { - String err; - src_pkg_name = find_export_template("osx.zip", &err); - if (src_pkg_name == "") { - EditorNode::add_io_error(err); - return ERR_FILE_NOT_FOUND; - } - } - - if (!DirAccess::exists(p_path.get_base_dir())) { - return ERR_FILE_BAD_PATH; - } - - FileAccess *src_f = nullptr; - zlib_filefunc_def io = zipio_create_io_from_file(&src_f); - - if (ep.step("Creating app", 0)) { - return ERR_SKIP; - } - - unzFile src_pkg_zip = unzOpen2(src_pkg_name.utf8().get_data(), &io); - if (!src_pkg_zip) { - EditorNode::add_io_error("Could not find template app to export:\n" + src_pkg_name); - return ERR_FILE_NOT_FOUND; - } - - int ret = unzGoToFirstFile(src_pkg_zip); - - String binary_to_use = "godot_osx_" + String(p_debug ? "debug" : "release") + ".64"; - - String pkg_name; - if (p_preset->get("application/name") != "") { - pkg_name = p_preset->get("application/name"); // app_name - } else if (String(ProjectSettings::get_singleton()->get("application/config/name")) != "") { - pkg_name = String(ProjectSettings::get_singleton()->get("application/config/name")); - } else { - pkg_name = "Unnamed"; - } - - pkg_name = OS::get_singleton()->get_safe_dir_name(pkg_name); - - String export_format = use_dmg() && p_path.ends_with("dmg") ? "dmg" : "zip"; - - // Create our application bundle. - String tmp_app_dir_name = pkg_name + ".app"; - String tmp_app_path_name = EditorPaths::get_singleton()->get_cache_dir().plus_file(tmp_app_dir_name); - print_line("Exporting to " + tmp_app_path_name); - - Error err = OK; - - DirAccessRef tmp_app_dir = DirAccess::create_for_path(tmp_app_path_name); - if (!tmp_app_dir) { - err = ERR_CANT_CREATE; - } - - // Create our folder structure. - if (err == OK) { - print_line("Creating " + tmp_app_path_name + "/Contents/MacOS"); - err = tmp_app_dir->make_dir_recursive(tmp_app_path_name + "/Contents/MacOS"); - } - - if (err == OK) { - print_line("Creating " + tmp_app_path_name + "/Contents/Frameworks"); - err = tmp_app_dir->make_dir_recursive(tmp_app_path_name + "/Contents/Frameworks"); - } - - if (err == OK) { - print_line("Creating " + tmp_app_path_name + "/Contents/Resources"); - err = tmp_app_dir->make_dir_recursive(tmp_app_path_name + "/Contents/Resources"); - } - - // Now process our template. - bool found_binary = false; - int total_size = 0; - Vector<String> dylibs_found; - - while (ret == UNZ_OK && err == OK) { - bool is_execute = false; - - // Get filename. - unz_file_info info; - char fname[16384]; - ret = unzGetCurrentFileInfo(src_pkg_zip, &info, fname, 16384, nullptr, 0, nullptr, 0); - - String file = fname; - - Vector<uint8_t> data; - data.resize(info.uncompressed_size); - - // Read. - unzOpenCurrentFile(src_pkg_zip); - unzReadCurrentFile(src_pkg_zip, data.ptrw(), data.size()); - unzCloseCurrentFile(src_pkg_zip); - - // Write. - file = file.replace_first("osx_template.app/", ""); - - if (file == "Contents/Info.plist") { - _fix_plist(p_preset, data, pkg_name); - } - - if (file.begins_with("Contents/MacOS/godot_")) { - if (file != "Contents/MacOS/" + binary_to_use) { - ret = unzGoToNextFile(src_pkg_zip); - continue; // skip - } - found_binary = true; - is_execute = true; - file = "Contents/MacOS/" + pkg_name; - } - - if (file == "Contents/Resources/icon.icns") { - // See if there is an icon. - String iconpath; - if (p_preset->get("application/icon") != "") { - iconpath = p_preset->get("application/icon"); - } else { - iconpath = ProjectSettings::get_singleton()->get("application/config/icon"); - } - - if (iconpath != "") { - if (iconpath.get_extension() == "icns") { - FileAccess *icon = FileAccess::open(iconpath, FileAccess::READ); - if (icon) { - data.resize(icon->get_length()); - icon->get_buffer(&data.write[0], icon->get_length()); - icon->close(); - memdelete(icon); - } - } else { - Ref<Image> icon; - icon.instantiate(); - icon->load(iconpath); - if (!icon->is_empty()) { - _make_icon(icon, data); - } - } - } - } - - if (data.size() > 0) { - if (file.find("/data.mono.osx.64.release_debug/") != -1) { - if (!p_debug) { - ret = unzGoToNextFile(src_pkg_zip); - continue; // skip - } - file = file.replace("/data.mono.osx.64.release_debug/", "/GodotSharp/"); - } - if (file.find("/data.mono.osx.64.release/") != -1) { - if (p_debug) { - ret = unzGoToNextFile(src_pkg_zip); - continue; // skip - } - file = file.replace("/data.mono.osx.64.release/", "/GodotSharp/"); - } - - if (file.ends_with(".dylib")) { - dylibs_found.push_back(file); - } - - print_line("ADDING: " + file + " size: " + itos(data.size())); - total_size += data.size(); - - // Write it into our application bundle. - file = tmp_app_path_name.plus_file(file); - if (err == OK) { - err = tmp_app_dir->make_dir_recursive(file.get_base_dir()); - } - if (err == OK) { - FileAccess *f = FileAccess::open(file, FileAccess::WRITE); - if (f) { - f->store_buffer(data.ptr(), data.size()); - f->close(); - if (is_execute) { - // chmod with 0755 if the file is executable. - FileAccess::set_unix_permissions(file, 0755); - } - memdelete(f); - } else { - err = ERR_CANT_CREATE; - } - } - } - - ret = unzGoToNextFile(src_pkg_zip); - } - - // We're done with our source zip. - unzClose(src_pkg_zip); - - if (!found_binary) { - ERR_PRINT("Requested template binary '" + binary_to_use + "' not found. It might be missing from your template archive."); - err = ERR_FILE_NOT_FOUND; - } - - if (err == OK) { - if (ep.step("Making PKG", 1)) { - return ERR_SKIP; - } - - String pack_path = tmp_app_path_name + "/Contents/Resources/" + pkg_name + ".pck"; - Vector<SharedObject> shared_objects; - err = save_pack(p_preset, pack_path, &shared_objects); - - // See if we can code sign our new package. - bool sign_enabled = p_preset->get("codesign/enable"); - - String ent_path = p_preset->get("codesign/entitlements/custom_file"); - if (sign_enabled && (ent_path == "")) { - ent_path = EditorPaths::get_singleton()->get_cache_dir().plus_file(pkg_name + ".entitlements"); - - FileAccess *ent_f = FileAccess::open(ent_path, FileAccess::WRITE); - if (ent_f) { - ent_f->store_line("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); - ent_f->store_line("<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">"); - ent_f->store_line("<plist version=\"1.0\">"); - ent_f->store_line("<dict>"); - if (Engine::get_singleton()->has_singleton("GodotSharp")) { - // These entitlements are required to run managed code, and are always enabled in Mono builds. - ent_f->store_line("<key>com.apple.security.cs.allow-jit</key>"); - ent_f->store_line("<true/>"); - ent_f->store_line("<key>com.apple.security.cs.allow-unsigned-executable-memory</key>"); - ent_f->store_line("<true/>"); - ent_f->store_line("<key>com.apple.security.cs.allow-dyld-environment-variables</key>"); - ent_f->store_line("<true/>"); - } else { - if ((bool)p_preset->get("codesign/entitlements/allow_jit_code_execution")) { - ent_f->store_line("<key>com.apple.security.cs.allow-jit</key>"); - ent_f->store_line("<true/>"); - } - if ((bool)p_preset->get("codesign/entitlements/allow_unsigned_executable_memory")) { - ent_f->store_line("<key>com.apple.security.cs.allow-unsigned-executable-memory</key>"); - ent_f->store_line("<true/>"); - } - if ((bool)p_preset->get("codesign/entitlements/allow_dyld_environment_variables")) { - ent_f->store_line("<key>com.apple.security.cs.allow-dyld-environment-variables</key>"); - ent_f->store_line("<true/>"); - } - } - - if ((bool)p_preset->get("codesign/entitlements/disable_library_validation")) { - ent_f->store_line("<key>com.apple.security.cs.disable-library-validation</key>"); - ent_f->store_line("<true/>"); - } - if ((bool)p_preset->get("codesign/entitlements/audio_input")) { - ent_f->store_line("<key>com.apple.security.device.audio-input</key>"); - ent_f->store_line("<true/>"); - } - if ((bool)p_preset->get("codesign/entitlements/camera")) { - ent_f->store_line("<key>com.apple.security.device.camera</key>"); - ent_f->store_line("<true/>"); - } - if ((bool)p_preset->get("codesign/entitlements/location")) { - ent_f->store_line("<key>com.apple.security.personal-information.location</key>"); - ent_f->store_line("<true/>"); - } - if ((bool)p_preset->get("codesign/entitlements/address_book")) { - ent_f->store_line("<key>com.apple.security.personal-information.addressbook</key>"); - ent_f->store_line("<true/>"); - } - if ((bool)p_preset->get("codesign/entitlements/calendars")) { - ent_f->store_line("<key>com.apple.security.personal-information.calendars</key>"); - ent_f->store_line("<true/>"); - } - if ((bool)p_preset->get("codesign/entitlements/photos_library")) { - ent_f->store_line("<key>com.apple.security.personal-information.photos-library</key>"); - ent_f->store_line("<true/>"); - } - if ((bool)p_preset->get("codesign/entitlements/apple_events")) { - ent_f->store_line("<key>com.apple.security.automation.apple-events</key>"); - ent_f->store_line("<true/>"); - } - if ((bool)p_preset->get("codesign/entitlements/debugging")) { - ent_f->store_line("<key>com.apple.security.get-task-allow</key>"); - ent_f->store_line("<true/>"); - } - - if ((bool)p_preset->get("codesign/entitlements/app_sandbox/enabled")) { - ent_f->store_line("<key>com.apple.security.app-sandbox</key>"); - ent_f->store_line("<true/>"); - - if ((bool)p_preset->get("codesign/entitlements/app_sandbox/network_server")) { - ent_f->store_line("<key>com.apple.security.network.server</key>"); - ent_f->store_line("<true/>"); - } - if ((bool)p_preset->get("codesign/entitlements/app_sandbox/network_client")) { - ent_f->store_line("<key>com.apple.security.network.client</key>"); - ent_f->store_line("<true/>"); - } - if ((bool)p_preset->get("codesign/entitlements/app_sandbox/device_usb")) { - ent_f->store_line("<key>com.apple.security.device.usb</key>"); - ent_f->store_line("<true/>"); - } - if ((bool)p_preset->get("codesign/entitlements/app_sandbox/device_bluetooth")) { - ent_f->store_line("<key>com.apple.security.device.bluetooth</key>"); - ent_f->store_line("<true/>"); - } - if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_downloads") == 1) { - ent_f->store_line("<key>com.apple.security.files.downloads.read-only</key>"); - ent_f->store_line("<true/>"); - } - if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_downloads") == 2) { - ent_f->store_line("<key>com.apple.security.files.downloads.read-write</key>"); - ent_f->store_line("<true/>"); - } - if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_pictures") == 1) { - ent_f->store_line("<key>com.apple.security.files.pictures.read-only</key>"); - ent_f->store_line("<true/>"); - } - if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_pictures") == 2) { - ent_f->store_line("<key>com.apple.security.files.pictures.read-write</key>"); - ent_f->store_line("<true/>"); - } - if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_music") == 1) { - ent_f->store_line("<key>com.apple.security.files.music.read-only</key>"); - ent_f->store_line("<true/>"); - } - if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_music") == 2) { - ent_f->store_line("<key>com.apple.security.files.music.read-write</key>"); - ent_f->store_line("<true/>"); - } - if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_movies") == 1) { - ent_f->store_line("<key>com.apple.security.files.movies.read-only</key>"); - ent_f->store_line("<true/>"); - } - if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_movies") == 2) { - ent_f->store_line("<key>com.apple.security.files.movies.read-write</key>"); - ent_f->store_line("<true/>"); - } - } - - ent_f->store_line("</dict>"); - ent_f->store_line("</plist>"); - - ent_f->close(); - memdelete(ent_f); - } else { - err = ERR_CANT_CREATE; - } - } - - if (err == OK) { - DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - for (int i = 0; i < shared_objects.size(); i++) { - String src_path = ProjectSettings::get_singleton()->globalize_path(shared_objects[i].path); - if (da->dir_exists(src_path)) { -#ifndef UNIX_ENABLED - WARN_PRINT("Relative symlinks are not supported, exported " + src_path.get_file() + " might be broken!"); -#endif - print_verbose("export framework: " + src_path + " -> " + tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file()); - err = da->make_dir_recursive(tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file()); - if (err == OK) { - err = da->copy_dir(src_path, tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file(), -1, true); - } - } else { - print_verbose("export dylib: " + src_path + " -> " + tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file()); - err = da->copy(src_path, tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file()); - } - if (err == OK && sign_enabled) { - err = _code_sign(p_preset, tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file(), ent_path); - } - } - memdelete(da); - } - - if (sign_enabled) { - for (int i = 0; i < dylibs_found.size(); i++) { - if (err == OK) { - err = _code_sign(p_preset, tmp_app_path_name + "/" + dylibs_found[i], ent_path); - } - } - } - - if (err == OK && sign_enabled) { - if (ep.step("Code signing bundle", 2)) { - return ERR_SKIP; - } - err = _code_sign(p_preset, tmp_app_path_name + "/Contents/MacOS/" + pkg_name, ent_path); - } - - if (export_format == "dmg") { - // Create a DMG. - if (err == OK) { - if (ep.step("Making DMG", 3)) { - return ERR_SKIP; - } - err = _create_dmg(p_path, pkg_name, tmp_app_path_name); - } - // Sign DMG. - if (err == OK && sign_enabled) { - if (ep.step("Code signing DMG", 3)) { - return ERR_SKIP; - } - err = _code_sign(p_preset, p_path, ent_path); - } - } else { - // Create ZIP. - if (err == OK) { - if (ep.step("Making ZIP", 3)) { - return ERR_SKIP; - } - if (FileAccess::exists(p_path)) { - OS::get_singleton()->move_to_trash(p_path); - } - - FileAccess *dst_f = nullptr; - zlib_filefunc_def io_dst = zipio_create_io_from_file(&dst_f); - zipFile zip = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io_dst); - - _zip_folder_recursive(zip, EditorPaths::get_singleton()->get_cache_dir(), pkg_name + ".app", pkg_name); - - zipClose(zip, nullptr); - } - } - - bool noto_enabled = p_preset->get("notarization/enable"); - if (err == OK && noto_enabled) { - if (ep.step("Sending archive for notarization", 4)) { - return ERR_SKIP; - } - err = _notarize(p_preset, p_path); - } - - // Clean up temporary .app dir. - tmp_app_dir->change_dir(tmp_app_path_name); - tmp_app_dir->erase_contents_recursive(); - tmp_app_dir->change_dir(".."); - tmp_app_dir->remove(tmp_app_dir_name); - } - - return err; -} - -void EditorExportPlatformOSX::_zip_folder_recursive(zipFile &p_zip, const String &p_root_path, const String &p_folder, const String &p_pkg_name) { - String dir = p_root_path.plus_file(p_folder); - - DirAccess *da = DirAccess::open(dir); - da->list_dir_begin(); - String f; - while ((f = da->get_next()) != "") { - if (f == "." || f == "..") { - continue; - } - if (da->is_link(f)) { - OS::Time time = OS::get_singleton()->get_time(); - OS::Date date = OS::get_singleton()->get_date(); - - zip_fileinfo zipfi; - zipfi.tmz_date.tm_hour = time.hour; - zipfi.tmz_date.tm_mday = date.day; - zipfi.tmz_date.tm_min = time.minute; - zipfi.tmz_date.tm_mon = date.month - 1; // Note: "tm" month range - 0..11, Godot month range - 1..12, http://www.cplusplus.com/reference/ctime/tm/ - zipfi.tmz_date.tm_sec = time.second; - zipfi.tmz_date.tm_year = date.year; - zipfi.dosDate = 0; - // 0120000: symbolic link type - // 0000755: permissions rwxr-xr-x - // 0000644: permissions rw-r--r-- - uint32_t _mode = 0120644; - zipfi.external_fa = (_mode << 16L) | !(_mode & 0200); - zipfi.internal_fa = 0; - - zipOpenNewFileInZip4(p_zip, - p_folder.plus_file(f).utf8().get_data(), - &zipfi, - nullptr, - 0, - nullptr, - 0, - nullptr, - Z_DEFLATED, - Z_DEFAULT_COMPRESSION, - 0, - -MAX_WBITS, - DEF_MEM_LEVEL, - Z_DEFAULT_STRATEGY, - nullptr, - 0, - 0x0314, // "version made by", 0x03 - Unix, 0x14 - ZIP specification version 2.0, required to store Unix file permissions - 0); - - String target = da->read_link(f); - zipWriteInFileInZip(p_zip, target.utf8().get_data(), target.utf8().size()); - zipCloseFileInZip(p_zip); - } else if (da->current_is_dir()) { - _zip_folder_recursive(p_zip, p_root_path, p_folder.plus_file(f), p_pkg_name); - } else { - bool is_executable = (p_folder.ends_with("MacOS") && (f == p_pkg_name)); - - OS::Time time = OS::get_singleton()->get_time(); - OS::Date date = OS::get_singleton()->get_date(); - - zip_fileinfo zipfi; - zipfi.tmz_date.tm_hour = time.hour; - zipfi.tmz_date.tm_mday = date.day; - zipfi.tmz_date.tm_min = time.minute; - zipfi.tmz_date.tm_mon = date.month - 1; // Note: "tm" month range - 0..11, Godot month range - 1..12, http://www.cplusplus.com/reference/ctime/tm/ - zipfi.tmz_date.tm_sec = time.second; - zipfi.tmz_date.tm_year = date.year; - zipfi.dosDate = 0; - // 0100000: regular file type - // 0000755: permissions rwxr-xr-x - // 0000644: permissions rw-r--r-- - uint32_t _mode = (is_executable ? 0100755 : 0100644); - zipfi.external_fa = (_mode << 16L) | !(_mode & 0200); - zipfi.internal_fa = 0; - - zipOpenNewFileInZip4(p_zip, - p_folder.plus_file(f).utf8().get_data(), - &zipfi, - nullptr, - 0, - nullptr, - 0, - nullptr, - Z_DEFLATED, - Z_DEFAULT_COMPRESSION, - 0, - -MAX_WBITS, - DEF_MEM_LEVEL, - Z_DEFAULT_STRATEGY, - nullptr, - 0, - 0x0314, // "version made by", 0x03 - Unix, 0x14 - ZIP specification version 2.0, required to store Unix file permissions - 0); - - Vector<uint8_t> array = FileAccess::get_file_as_array(dir.plus_file(f)); - zipWriteInFileInZip(p_zip, array.ptr(), array.size()); - zipCloseFileInZip(p_zip); - } - } - da->list_dir_end(); - memdelete(da); -} - -bool EditorExportPlatformOSX::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const { - String err; - bool valid = false; - - // Look for export templates (first official, and if defined custom templates). - - bool dvalid = exists_export_template("osx.zip", &err); - bool rvalid = dvalid; // Both in the same ZIP. - - if (p_preset->get("custom_template/debug") != "") { - dvalid = FileAccess::exists(p_preset->get("custom_template/debug")); - if (!dvalid) { - err += TTR("Custom debug template not found.") + "\n"; - } - } - if (p_preset->get("custom_template/release") != "") { - rvalid = FileAccess::exists(p_preset->get("custom_template/release")); - if (!rvalid) { - err += TTR("Custom release template not found.") + "\n"; - } - } - - valid = dvalid || rvalid; - r_missing_templates = !valid; - - String identifier = p_preset->get("application/bundle_identifier"); - String pn_err; - if (!is_package_name_valid(identifier, &pn_err)) { - err += TTR("Invalid bundle identifier:") + " " + pn_err + "\n"; - valid = false; - } - - bool sign_enabled = p_preset->get("codesign/enable"); - bool noto_enabled = p_preset->get("notarization/enable"); - if (noto_enabled) { - if (!sign_enabled) { - err += TTR("Notarization: code signing required.") + "\n"; - valid = false; - } - bool hr_enabled = p_preset->get("codesign/hardened_runtime"); - if (!hr_enabled) { - err += TTR("Notarization: hardened runtime required.") + "\n"; - valid = false; - } - if (p_preset->get("notarization/apple_id_name") == "") { - err += TTR("Notarization: Apple ID name not specified.") + "\n"; - valid = false; - } - if (p_preset->get("notarization/apple_id_password") == "") { - err += TTR("Notarization: Apple ID password not specified.") + "\n"; - valid = false; - } - } - - if (!err.is_empty()) { - r_error = err; - } - return valid; -} - -EditorExportPlatformOSX::EditorExportPlatformOSX() { - Ref<Image> img = memnew(Image(_osx_logo)); - logo.instantiate(); - logo->create_from_image(img); -} - -EditorExportPlatformOSX::~EditorExportPlatformOSX() { -} +#include "export_plugin.h" void register_osx_exporter() { Ref<EditorExportPlatformOSX> platform; diff --git a/platform/osx/export/export_plugin.cpp b/platform/osx/export/export_plugin.cpp new file mode 100644 index 0000000000..53ae1ea6fe --- /dev/null +++ b/platform/osx/export/export_plugin.cpp @@ -0,0 +1,1085 @@ +/*************************************************************************/ +/* export_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "export_plugin.h" + +void EditorExportPlatformOSX::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) { + if (p_preset->get("texture_format/s3tc")) { + r_features->push_back("s3tc"); + } + if (p_preset->get("texture_format/etc")) { + r_features->push_back("etc"); + } + if (p_preset->get("texture_format/etc2")) { + r_features->push_back("etc2"); + } + + r_features->push_back("64"); +} + +void EditorExportPlatformOSX::get_export_options(List<ExportOption> *r_options) { + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), "")); + + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/info"), "Made with Godot Engine")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_FILE, "*.png,*.icns"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/bundle_identifier", PROPERTY_HINT_PLACEHOLDER_TEXT, "com.example.game"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/signature"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/app_category", PROPERTY_HINT_ENUM, "Business,Developer-tools,Education,Entertainment,Finance,Games,Action-games,Adventure-games,Arcade-games,Board-games,Card-games,Casino-games,Dice-games,Educational-games,Family-games,Kids-games,Music-games,Puzzle-games,Racing-games,Role-playing-games,Simulation-games,Sports-games,Strategy-games,Trivia-games,Word-games,Graphics-design,Healthcare-fitness,Lifestyle,Medical,Music,News,Photography,Productivity,Reference,Social-networking,Sports,Travel,Utilities,Video,Weather"), "Games")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/short_version"), "1.0")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/version"), "1.0")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/copyright"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "display/high_res"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/camera_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the camera"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/microphone_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the microphone"), "")); + +#ifdef OSX_ENABLED + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/enable"), true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/identity", PROPERTY_HINT_PLACEHOLDER_TEXT, "Type: Name (ID)"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/timestamp"), true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/hardened_runtime"), true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/replace_existing_signature"), true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/entitlements/custom_file", PROPERTY_HINT_GLOBAL_FILE, "*.plist"), "")); + + if (!Engine::get_singleton()->has_singleton("GodotSharp")) { + // These entitlements are required to run managed code, and are always enabled in Mono builds. + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/allow_jit_code_execution"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/allow_unsigned_executable_memory"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/allow_dyld_environment_variables"), false)); + } + + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/disable_library_validation"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/audio_input"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/camera"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/location"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/address_book"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/calendars"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/photos_library"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/apple_events"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/debugging"), false)); + + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/enabled"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/network_server"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/network_client"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/device_usb"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/app_sandbox/device_bluetooth"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_downloads", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0)); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_pictures", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0)); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_music", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0)); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/entitlements/app_sandbox/files_movies", PROPERTY_HINT_ENUM, "No,Read-only,Read-write"), 0)); + + r_options->push_back(ExportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "codesign/custom_options"), PackedStringArray())); + + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "notarization/enable"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_id_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Apple ID email"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_id_password", PROPERTY_HINT_PLACEHOLDER_TEXT, "Enable two-factor authentication and provide app-specific password"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_team_id", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide team ID if your Apple ID belongs to multiple teams"), "")); +#endif + + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/s3tc"), true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc2"), false)); +} + +void _rgba8_to_packbits_encode(int p_ch, int p_size, Vector<uint8_t> &p_source, Vector<uint8_t> &p_dest) { + int src_len = p_size * p_size; + + Vector<uint8_t> result; + result.resize(src_len * 1.25); //temp vector for rle encoded data, make it 25% larger for worst case scenario + int res_size = 0; + + uint8_t buf[128]; + int buf_size = 0; + + int i = 0; + while (i < src_len) { + uint8_t cur = p_source.ptr()[i * 4 + p_ch]; + + if (i < src_len - 2) { + if ((p_source.ptr()[(i + 1) * 4 + p_ch] == cur) && (p_source.ptr()[(i + 2) * 4 + p_ch] == cur)) { + if (buf_size > 0) { + result.write[res_size++] = (uint8_t)(buf_size - 1); + memcpy(&result.write[res_size], &buf, buf_size); + res_size += buf_size; + buf_size = 0; + } + + uint8_t lim = i + 130 >= src_len ? src_len - i - 1 : 130; + bool hit_lim = true; + + for (int j = 3; j <= lim; j++) { + if (p_source.ptr()[(i + j) * 4 + p_ch] != cur) { + hit_lim = false; + i = i + j - 1; + result.write[res_size++] = (uint8_t)(j - 3 + 0x80); + result.write[res_size++] = cur; + break; + } + } + if (hit_lim) { + result.write[res_size++] = (uint8_t)(lim - 3 + 0x80); + result.write[res_size++] = cur; + i = i + lim; + } + } else { + buf[buf_size++] = cur; + if (buf_size == 128) { + result.write[res_size++] = (uint8_t)(buf_size - 1); + memcpy(&result.write[res_size], &buf, buf_size); + res_size += buf_size; + buf_size = 0; + } + } + } else { + buf[buf_size++] = cur; + result.write[res_size++] = (uint8_t)(buf_size - 1); + memcpy(&result.write[res_size], &buf, buf_size); + res_size += buf_size; + buf_size = 0; + } + + i++; + } + + int ofs = p_dest.size(); + p_dest.resize(p_dest.size() + res_size); + memcpy(&p_dest.write[ofs], result.ptr(), res_size); +} + +void EditorExportPlatformOSX::_make_icon(const Ref<Image> &p_icon, Vector<uint8_t> &p_data) { + Ref<ImageTexture> it = memnew(ImageTexture); + + Vector<uint8_t> data; + + data.resize(8); + data.write[0] = 'i'; + data.write[1] = 'c'; + data.write[2] = 'n'; + data.write[3] = 's'; + + struct MacOSIconInfo { + const char *name; + const char *mask_name; + bool is_png; + int size; + }; + + static const MacOSIconInfo icon_infos[] = { + { "ic10", "", true, 1024 }, //1024x1024 32-bit PNG and 512x512@2x 32-bit "retina" PNG + { "ic09", "", true, 512 }, //512×512 32-bit PNG + { "ic14", "", true, 512 }, //256x256@2x 32-bit "retina" PNG + { "ic08", "", true, 256 }, //256×256 32-bit PNG + { "ic13", "", true, 256 }, //128x128@2x 32-bit "retina" PNG + { "ic07", "", true, 128 }, //128x128 32-bit PNG + { "ic12", "", true, 64 }, //32x32@2x 32-bit "retina" PNG + { "ic11", "", true, 32 }, //16x16@2x 32-bit "retina" PNG + { "il32", "l8mk", false, 32 }, //32x32 24-bit RLE + 8-bit uncompressed mask + { "is32", "s8mk", false, 16 } //16x16 24-bit RLE + 8-bit uncompressed mask + }; + + for (uint64_t i = 0; i < (sizeof(icon_infos) / sizeof(icon_infos[0])); ++i) { + Ref<Image> copy = p_icon; // does this make sense? doesn't this just increase the reference count instead of making a copy? Do we even need a copy? + copy->convert(Image::FORMAT_RGBA8); + copy->resize(icon_infos[i].size, icon_infos[i].size); + + if (icon_infos[i].is_png) { + // Encode PNG icon. + it->create_from_image(copy); + String path = EditorPaths::get_singleton()->get_cache_dir().plus_file("icon.png"); + ResourceSaver::save(path, it); + + FileAccess *f = FileAccess::open(path, FileAccess::READ); + if (!f) { + // Clean up generated file. + DirAccess::remove_file_or_error(path); + ERR_FAIL(); + } + + int ofs = data.size(); + uint64_t len = f->get_length(); + data.resize(data.size() + len + 8); + f->get_buffer(&data.write[ofs + 8], len); + memdelete(f); + len += 8; + len = BSWAP32(len); + memcpy(&data.write[ofs], icon_infos[i].name, 4); + encode_uint32(len, &data.write[ofs + 4]); + + // Clean up generated file. + DirAccess::remove_file_or_error(path); + + } else { + Vector<uint8_t> src_data = copy->get_data(); + + //encode 24bit RGB RLE icon + { + int ofs = data.size(); + data.resize(data.size() + 8); + + _rgba8_to_packbits_encode(0, icon_infos[i].size, src_data, data); // encode R + _rgba8_to_packbits_encode(1, icon_infos[i].size, src_data, data); // encode G + _rgba8_to_packbits_encode(2, icon_infos[i].size, src_data, data); // encode B + + int len = data.size() - ofs; + len = BSWAP32(len); + memcpy(&data.write[ofs], icon_infos[i].name, 4); + encode_uint32(len, &data.write[ofs + 4]); + } + + //encode 8bit mask uncompressed icon + { + int ofs = data.size(); + int len = copy->get_width() * copy->get_height(); + data.resize(data.size() + len + 8); + + for (int j = 0; j < len; j++) { + data.write[ofs + 8 + j] = src_data.ptr()[j * 4 + 3]; + } + len += 8; + len = BSWAP32(len); + memcpy(&data.write[ofs], icon_infos[i].mask_name, 4); + encode_uint32(len, &data.write[ofs + 4]); + } + } + } + + uint32_t total_len = data.size(); + total_len = BSWAP32(total_len); + encode_uint32(total_len, &data.write[4]); + + p_data = data; +} + +void EditorExportPlatformOSX::_fix_plist(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &plist, const String &p_binary) { + String str; + String strnew; + str.parse_utf8((const char *)plist.ptr(), plist.size()); + Vector<String> lines = str.split("\n"); + for (int i = 0; i < lines.size(); i++) { + if (lines[i].find("$binary") != -1) { + strnew += lines[i].replace("$binary", p_binary) + "\n"; + } else if (lines[i].find("$name") != -1) { + strnew += lines[i].replace("$name", p_binary) + "\n"; + } else if (lines[i].find("$info") != -1) { + strnew += lines[i].replace("$info", p_preset->get("application/info")) + "\n"; + } else if (lines[i].find("$bundle_identifier") != -1) { + strnew += lines[i].replace("$bundle_identifier", p_preset->get("application/bundle_identifier")) + "\n"; + } else if (lines[i].find("$short_version") != -1) { + strnew += lines[i].replace("$short_version", p_preset->get("application/short_version")) + "\n"; + } else if (lines[i].find("$version") != -1) { + strnew += lines[i].replace("$version", p_preset->get("application/version")) + "\n"; + } else if (lines[i].find("$signature") != -1) { + strnew += lines[i].replace("$signature", p_preset->get("application/signature")) + "\n"; + } else if (lines[i].find("$app_category") != -1) { + String cat = p_preset->get("application/app_category"); + strnew += lines[i].replace("$app_category", cat.to_lower()) + "\n"; + } else if (lines[i].find("$copyright") != -1) { + strnew += lines[i].replace("$copyright", p_preset->get("application/copyright")) + "\n"; + } else if (lines[i].find("$highres") != -1) { + strnew += lines[i].replace("$highres", p_preset->get("display/high_res") ? "<true/>" : "<false/>") + "\n"; + } else if (lines[i].find("$camera_usage_description") != -1) { + String description = p_preset->get("privacy/camera_usage_description"); + strnew += lines[i].replace("$camera_usage_description", description) + "\n"; + } else if (lines[i].find("$microphone_usage_description") != -1) { + String description = p_preset->get("privacy/microphone_usage_description"); + strnew += lines[i].replace("$microphone_usage_description", description) + "\n"; + } else { + strnew += lines[i] + "\n"; + } + } + + CharString cs = strnew.utf8(); + plist.resize(cs.size() - 1); + for (int i = 0; i < cs.size() - 1; i++) { + plist.write[i] = cs[i]; + } +} + +/** + If we're running the OSX version of the Godot editor we'll: + - export our application bundle to a temporary folder + - attempt to code sign it + - and then wrap it up in a DMG +**/ + +Error EditorExportPlatformOSX::_notarize(const Ref<EditorExportPreset> &p_preset, const String &p_path) { +#ifdef OSX_ENABLED + List<String> args; + + args.push_back("altool"); + args.push_back("--notarize-app"); + + args.push_back("--primary-bundle-id"); + args.push_back(p_preset->get("application/bundle_identifier")); + + args.push_back("--username"); + args.push_back(p_preset->get("notarization/apple_id_name")); + + args.push_back("--password"); + args.push_back(p_preset->get("notarization/apple_id_password")); + + args.push_back("--type"); + args.push_back("osx"); + + if (p_preset->get("notarization/apple_team_id")) { + args.push_back("--asc-provider"); + args.push_back(p_preset->get("notarization/apple_team_id")); + } + + args.push_back("--file"); + args.push_back(p_path); + + String str; + Error err = OS::get_singleton()->execute("xcrun", args, &str, nullptr, true); + ERR_FAIL_COND_V(err != OK, err); + + print_line("altool (" + p_path + "):\n" + str); + if (str.find("RequestUUID") == -1) { + EditorNode::add_io_error("altool: " + str); + return FAILED; + } else { + print_line("Note: The notarization process generally takes less than an hour. When the process is completed, you'll receive an email."); + print_line(" You can check progress manually by opening a Terminal and running the following command:"); + print_line(" \"xcrun altool --notarization-history 0 -u <your email> -p <app-specific pwd>\""); + } + +#endif + + return OK; +} + +Error EditorExportPlatformOSX::_code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path, const String &p_ent_path) { +#ifdef OSX_ENABLED + List<String> args; + + if (p_preset->get("codesign/timestamp")) { + args.push_back("--timestamp"); + } + if (p_preset->get("codesign/hardened_runtime")) { + args.push_back("--options"); + args.push_back("runtime"); + } + + if (p_path.get_extension() != "dmg") { + args.push_back("--entitlements"); + args.push_back(p_ent_path); + } + + PackedStringArray user_args = p_preset->get("codesign/custom_options"); + for (int i = 0; i < user_args.size(); i++) { + String user_arg = user_args[i].strip_edges(); + if (!user_arg.is_empty()) { + args.push_back(user_arg); + } + } + + args.push_back("-s"); + if (p_preset->get("codesign/identity") == "") { + args.push_back("-"); + } else { + args.push_back(p_preset->get("codesign/identity")); + } + + args.push_back("-v"); /* provide some more feedback */ + + if (p_preset->get("codesign/replace_existing_signature")) { + args.push_back("-f"); + } + + args.push_back(p_path); + + String str; + Error err = OS::get_singleton()->execute("codesign", args, &str, nullptr, true); + ERR_FAIL_COND_V(err != OK, err); + + print_line("codesign (" + p_path + "):\n" + str); + if (str.find("no identity found") != -1) { + EditorNode::add_io_error("codesign: no identity found"); + return FAILED; + } + if ((str.find("unrecognized blob type") != -1) || (str.find("cannot read entitlement data") != -1)) { + EditorNode::add_io_error("codesign: invalid entitlements file"); + return FAILED; + } +#endif + + return OK; +} + +Error EditorExportPlatformOSX::_create_dmg(const String &p_dmg_path, const String &p_pkg_name, const String &p_app_path_name) { + List<String> args; + + if (FileAccess::exists(p_dmg_path)) { + OS::get_singleton()->move_to_trash(p_dmg_path); + } + + args.push_back("create"); + args.push_back(p_dmg_path); + args.push_back("-volname"); + args.push_back(p_pkg_name); + args.push_back("-fs"); + args.push_back("HFS+"); + args.push_back("-srcfolder"); + args.push_back(p_app_path_name); + + String str; + Error err = OS::get_singleton()->execute("hdiutil", args, &str, nullptr, true); + ERR_FAIL_COND_V(err != OK, err); + + print_line("hdiutil returned: " + str); + if (str.find("create failed") != -1) { + if (str.find("File exists") != -1) { + EditorNode::add_io_error("hdiutil: create failed - file exists"); + } else { + EditorNode::add_io_error("hdiutil: create failed"); + } + return FAILED; + } + + return OK; +} + +Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { + ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags); + + String src_pkg_name; + + EditorProgress ep("export", "Exporting for OSX", 3, true); + + if (p_debug) { + src_pkg_name = p_preset->get("custom_template/debug"); + } else { + src_pkg_name = p_preset->get("custom_template/release"); + } + + if (src_pkg_name == "") { + String err; + src_pkg_name = find_export_template("osx.zip", &err); + if (src_pkg_name == "") { + EditorNode::add_io_error(err); + return ERR_FILE_NOT_FOUND; + } + } + + if (!DirAccess::exists(p_path.get_base_dir())) { + return ERR_FILE_BAD_PATH; + } + + FileAccess *src_f = nullptr; + zlib_filefunc_def io = zipio_create_io_from_file(&src_f); + + if (ep.step("Creating app", 0)) { + return ERR_SKIP; + } + + unzFile src_pkg_zip = unzOpen2(src_pkg_name.utf8().get_data(), &io); + if (!src_pkg_zip) { + EditorNode::add_io_error("Could not find template app to export:\n" + src_pkg_name); + return ERR_FILE_NOT_FOUND; + } + + int ret = unzGoToFirstFile(src_pkg_zip); + + String binary_to_use = "godot_osx_" + String(p_debug ? "debug" : "release") + ".64"; + + String pkg_name; + if (p_preset->get("application/name") != "") { + pkg_name = p_preset->get("application/name"); // app_name + } else if (String(ProjectSettings::get_singleton()->get("application/config/name")) != "") { + pkg_name = String(ProjectSettings::get_singleton()->get("application/config/name")); + } else { + pkg_name = "Unnamed"; + } + + pkg_name = OS::get_singleton()->get_safe_dir_name(pkg_name); + + String export_format = use_dmg() && p_path.ends_with("dmg") ? "dmg" : "zip"; + + // Create our application bundle. + String tmp_app_dir_name = pkg_name + ".app"; + String tmp_app_path_name = EditorPaths::get_singleton()->get_cache_dir().plus_file(tmp_app_dir_name); + print_line("Exporting to " + tmp_app_path_name); + + Error err = OK; + + DirAccessRef tmp_app_dir = DirAccess::create_for_path(tmp_app_path_name); + if (!tmp_app_dir) { + err = ERR_CANT_CREATE; + } + + // Create our folder structure. + if (err == OK) { + print_line("Creating " + tmp_app_path_name + "/Contents/MacOS"); + err = tmp_app_dir->make_dir_recursive(tmp_app_path_name + "/Contents/MacOS"); + } + + if (err == OK) { + print_line("Creating " + tmp_app_path_name + "/Contents/Frameworks"); + err = tmp_app_dir->make_dir_recursive(tmp_app_path_name + "/Contents/Frameworks"); + } + + if (err == OK) { + print_line("Creating " + tmp_app_path_name + "/Contents/Resources"); + err = tmp_app_dir->make_dir_recursive(tmp_app_path_name + "/Contents/Resources"); + } + + // Now process our template. + bool found_binary = false; + int total_size = 0; + Vector<String> dylibs_found; + + while (ret == UNZ_OK && err == OK) { + bool is_execute = false; + + // Get filename. + unz_file_info info; + char fname[16384]; + ret = unzGetCurrentFileInfo(src_pkg_zip, &info, fname, 16384, nullptr, 0, nullptr, 0); + + String file = fname; + + Vector<uint8_t> data; + data.resize(info.uncompressed_size); + + // Read. + unzOpenCurrentFile(src_pkg_zip); + unzReadCurrentFile(src_pkg_zip, data.ptrw(), data.size()); + unzCloseCurrentFile(src_pkg_zip); + + // Write. + file = file.replace_first("osx_template.app/", ""); + + if (file == "Contents/Info.plist") { + _fix_plist(p_preset, data, pkg_name); + } + + if (file.begins_with("Contents/MacOS/godot_")) { + if (file != "Contents/MacOS/" + binary_to_use) { + ret = unzGoToNextFile(src_pkg_zip); + continue; // skip + } + found_binary = true; + is_execute = true; + file = "Contents/MacOS/" + pkg_name; + } + + if (file == "Contents/Resources/icon.icns") { + // See if there is an icon. + String iconpath; + if (p_preset->get("application/icon") != "") { + iconpath = p_preset->get("application/icon"); + } else { + iconpath = ProjectSettings::get_singleton()->get("application/config/icon"); + } + + if (iconpath != "") { + if (iconpath.get_extension() == "icns") { + FileAccess *icon = FileAccess::open(iconpath, FileAccess::READ); + if (icon) { + data.resize(icon->get_length()); + icon->get_buffer(&data.write[0], icon->get_length()); + icon->close(); + memdelete(icon); + } + } else { + Ref<Image> icon; + icon.instantiate(); + icon->load(iconpath); + if (!icon->is_empty()) { + _make_icon(icon, data); + } + } + } + } + + if (data.size() > 0) { + if (file.find("/data.mono.osx.64.release_debug/") != -1) { + if (!p_debug) { + ret = unzGoToNextFile(src_pkg_zip); + continue; // skip + } + file = file.replace("/data.mono.osx.64.release_debug/", "/GodotSharp/"); + } + if (file.find("/data.mono.osx.64.release/") != -1) { + if (p_debug) { + ret = unzGoToNextFile(src_pkg_zip); + continue; // skip + } + file = file.replace("/data.mono.osx.64.release/", "/GodotSharp/"); + } + + if (file.ends_with(".dylib")) { + dylibs_found.push_back(file); + } + + print_line("ADDING: " + file + " size: " + itos(data.size())); + total_size += data.size(); + + // Write it into our application bundle. + file = tmp_app_path_name.plus_file(file); + if (err == OK) { + err = tmp_app_dir->make_dir_recursive(file.get_base_dir()); + } + if (err == OK) { + FileAccess *f = FileAccess::open(file, FileAccess::WRITE); + if (f) { + f->store_buffer(data.ptr(), data.size()); + f->close(); + if (is_execute) { + // chmod with 0755 if the file is executable. + FileAccess::set_unix_permissions(file, 0755); + } + memdelete(f); + } else { + err = ERR_CANT_CREATE; + } + } + } + + ret = unzGoToNextFile(src_pkg_zip); + } + + // We're done with our source zip. + unzClose(src_pkg_zip); + + if (!found_binary) { + ERR_PRINT("Requested template binary '" + binary_to_use + "' not found. It might be missing from your template archive."); + err = ERR_FILE_NOT_FOUND; + } + + if (err == OK) { + if (ep.step("Making PKG", 1)) { + return ERR_SKIP; + } + + String pack_path = tmp_app_path_name + "/Contents/Resources/" + pkg_name + ".pck"; + Vector<SharedObject> shared_objects; + err = save_pack(p_preset, pack_path, &shared_objects); + + // See if we can code sign our new package. + bool sign_enabled = p_preset->get("codesign/enable"); + + String ent_path = p_preset->get("codesign/entitlements/custom_file"); + if (sign_enabled && (ent_path == "")) { + ent_path = EditorPaths::get_singleton()->get_cache_dir().plus_file(pkg_name + ".entitlements"); + + FileAccess *ent_f = FileAccess::open(ent_path, FileAccess::WRITE); + if (ent_f) { + ent_f->store_line("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); + ent_f->store_line("<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">"); + ent_f->store_line("<plist version=\"1.0\">"); + ent_f->store_line("<dict>"); + if (Engine::get_singleton()->has_singleton("GodotSharp")) { + // These entitlements are required to run managed code, and are always enabled in Mono builds. + ent_f->store_line("<key>com.apple.security.cs.allow-jit</key>"); + ent_f->store_line("<true/>"); + ent_f->store_line("<key>com.apple.security.cs.allow-unsigned-executable-memory</key>"); + ent_f->store_line("<true/>"); + ent_f->store_line("<key>com.apple.security.cs.allow-dyld-environment-variables</key>"); + ent_f->store_line("<true/>"); + } else { + if ((bool)p_preset->get("codesign/entitlements/allow_jit_code_execution")) { + ent_f->store_line("<key>com.apple.security.cs.allow-jit</key>"); + ent_f->store_line("<true/>"); + } + if ((bool)p_preset->get("codesign/entitlements/allow_unsigned_executable_memory")) { + ent_f->store_line("<key>com.apple.security.cs.allow-unsigned-executable-memory</key>"); + ent_f->store_line("<true/>"); + } + if ((bool)p_preset->get("codesign/entitlements/allow_dyld_environment_variables")) { + ent_f->store_line("<key>com.apple.security.cs.allow-dyld-environment-variables</key>"); + ent_f->store_line("<true/>"); + } + } + + if ((bool)p_preset->get("codesign/entitlements/disable_library_validation")) { + ent_f->store_line("<key>com.apple.security.cs.disable-library-validation</key>"); + ent_f->store_line("<true/>"); + } + if ((bool)p_preset->get("codesign/entitlements/audio_input")) { + ent_f->store_line("<key>com.apple.security.device.audio-input</key>"); + ent_f->store_line("<true/>"); + } + if ((bool)p_preset->get("codesign/entitlements/camera")) { + ent_f->store_line("<key>com.apple.security.device.camera</key>"); + ent_f->store_line("<true/>"); + } + if ((bool)p_preset->get("codesign/entitlements/location")) { + ent_f->store_line("<key>com.apple.security.personal-information.location</key>"); + ent_f->store_line("<true/>"); + } + if ((bool)p_preset->get("codesign/entitlements/address_book")) { + ent_f->store_line("<key>com.apple.security.personal-information.addressbook</key>"); + ent_f->store_line("<true/>"); + } + if ((bool)p_preset->get("codesign/entitlements/calendars")) { + ent_f->store_line("<key>com.apple.security.personal-information.calendars</key>"); + ent_f->store_line("<true/>"); + } + if ((bool)p_preset->get("codesign/entitlements/photos_library")) { + ent_f->store_line("<key>com.apple.security.personal-information.photos-library</key>"); + ent_f->store_line("<true/>"); + } + if ((bool)p_preset->get("codesign/entitlements/apple_events")) { + ent_f->store_line("<key>com.apple.security.automation.apple-events</key>"); + ent_f->store_line("<true/>"); + } + if ((bool)p_preset->get("codesign/entitlements/debugging")) { + ent_f->store_line("<key>com.apple.security.get-task-allow</key>"); + ent_f->store_line("<true/>"); + } + + if ((bool)p_preset->get("codesign/entitlements/app_sandbox/enabled")) { + ent_f->store_line("<key>com.apple.security.app-sandbox</key>"); + ent_f->store_line("<true/>"); + + if ((bool)p_preset->get("codesign/entitlements/app_sandbox/network_server")) { + ent_f->store_line("<key>com.apple.security.network.server</key>"); + ent_f->store_line("<true/>"); + } + if ((bool)p_preset->get("codesign/entitlements/app_sandbox/network_client")) { + ent_f->store_line("<key>com.apple.security.network.client</key>"); + ent_f->store_line("<true/>"); + } + if ((bool)p_preset->get("codesign/entitlements/app_sandbox/device_usb")) { + ent_f->store_line("<key>com.apple.security.device.usb</key>"); + ent_f->store_line("<true/>"); + } + if ((bool)p_preset->get("codesign/entitlements/app_sandbox/device_bluetooth")) { + ent_f->store_line("<key>com.apple.security.device.bluetooth</key>"); + ent_f->store_line("<true/>"); + } + if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_downloads") == 1) { + ent_f->store_line("<key>com.apple.security.files.downloads.read-only</key>"); + ent_f->store_line("<true/>"); + } + if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_downloads") == 2) { + ent_f->store_line("<key>com.apple.security.files.downloads.read-write</key>"); + ent_f->store_line("<true/>"); + } + if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_pictures") == 1) { + ent_f->store_line("<key>com.apple.security.files.pictures.read-only</key>"); + ent_f->store_line("<true/>"); + } + if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_pictures") == 2) { + ent_f->store_line("<key>com.apple.security.files.pictures.read-write</key>"); + ent_f->store_line("<true/>"); + } + if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_music") == 1) { + ent_f->store_line("<key>com.apple.security.files.music.read-only</key>"); + ent_f->store_line("<true/>"); + } + if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_music") == 2) { + ent_f->store_line("<key>com.apple.security.files.music.read-write</key>"); + ent_f->store_line("<true/>"); + } + if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_movies") == 1) { + ent_f->store_line("<key>com.apple.security.files.movies.read-only</key>"); + ent_f->store_line("<true/>"); + } + if ((int)p_preset->get("codesign/entitlements/app_sandbox/files_movies") == 2) { + ent_f->store_line("<key>com.apple.security.files.movies.read-write</key>"); + ent_f->store_line("<true/>"); + } + } + + ent_f->store_line("</dict>"); + ent_f->store_line("</plist>"); + + ent_f->close(); + memdelete(ent_f); + } else { + err = ERR_CANT_CREATE; + } + } + + if (err == OK) { + DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + for (int i = 0; i < shared_objects.size(); i++) { + String src_path = ProjectSettings::get_singleton()->globalize_path(shared_objects[i].path); + if (da->dir_exists(src_path)) { +#ifndef UNIX_ENABLED + WARN_PRINT("Relative symlinks are not supported, exported " + src_path.get_file() + " might be broken!"); +#endif + print_verbose("export framework: " + src_path + " -> " + tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file()); + err = da->make_dir_recursive(tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file()); + if (err == OK) { + err = da->copy_dir(src_path, tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file(), -1, true); + } + } else { + print_verbose("export dylib: " + src_path + " -> " + tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file()); + err = da->copy(src_path, tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file()); + } + if (err == OK && sign_enabled) { + err = _code_sign(p_preset, tmp_app_path_name + "/Contents/Frameworks/" + src_path.get_file(), ent_path); + } + } + memdelete(da); + } + + if (sign_enabled) { + for (int i = 0; i < dylibs_found.size(); i++) { + if (err == OK) { + err = _code_sign(p_preset, tmp_app_path_name + "/" + dylibs_found[i], ent_path); + } + } + } + + if (err == OK && sign_enabled) { + if (ep.step("Code signing bundle", 2)) { + return ERR_SKIP; + } + err = _code_sign(p_preset, tmp_app_path_name + "/Contents/MacOS/" + pkg_name, ent_path); + } + + if (export_format == "dmg") { + // Create a DMG. + if (err == OK) { + if (ep.step("Making DMG", 3)) { + return ERR_SKIP; + } + err = _create_dmg(p_path, pkg_name, tmp_app_path_name); + } + // Sign DMG. + if (err == OK && sign_enabled) { + if (ep.step("Code signing DMG", 3)) { + return ERR_SKIP; + } + err = _code_sign(p_preset, p_path, ent_path); + } + } else { + // Create ZIP. + if (err == OK) { + if (ep.step("Making ZIP", 3)) { + return ERR_SKIP; + } + if (FileAccess::exists(p_path)) { + OS::get_singleton()->move_to_trash(p_path); + } + + FileAccess *dst_f = nullptr; + zlib_filefunc_def io_dst = zipio_create_io_from_file(&dst_f); + zipFile zip = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io_dst); + + _zip_folder_recursive(zip, EditorPaths::get_singleton()->get_cache_dir(), pkg_name + ".app", pkg_name); + + zipClose(zip, nullptr); + } + } + + bool noto_enabled = p_preset->get("notarization/enable"); + if (err == OK && noto_enabled) { + if (ep.step("Sending archive for notarization", 4)) { + return ERR_SKIP; + } + err = _notarize(p_preset, p_path); + } + + // Clean up temporary .app dir. + tmp_app_dir->change_dir(tmp_app_path_name); + tmp_app_dir->erase_contents_recursive(); + tmp_app_dir->change_dir(".."); + tmp_app_dir->remove(tmp_app_dir_name); + } + + return err; +} + +void EditorExportPlatformOSX::_zip_folder_recursive(zipFile &p_zip, const String &p_root_path, const String &p_folder, const String &p_pkg_name) { + String dir = p_root_path.plus_file(p_folder); + + DirAccess *da = DirAccess::open(dir); + da->list_dir_begin(); + String f; + while ((f = da->get_next()) != "") { + if (f == "." || f == "..") { + continue; + } + if (da->is_link(f)) { + OS::Time time = OS::get_singleton()->get_time(); + OS::Date date = OS::get_singleton()->get_date(); + + zip_fileinfo zipfi; + zipfi.tmz_date.tm_hour = time.hour; + zipfi.tmz_date.tm_mday = date.day; + zipfi.tmz_date.tm_min = time.minute; + zipfi.tmz_date.tm_mon = date.month - 1; // Note: "tm" month range - 0..11, Godot month range - 1..12, http://www.cplusplus.com/reference/ctime/tm/ + zipfi.tmz_date.tm_sec = time.second; + zipfi.tmz_date.tm_year = date.year; + zipfi.dosDate = 0; + // 0120000: symbolic link type + // 0000755: permissions rwxr-xr-x + // 0000644: permissions rw-r--r-- + uint32_t _mode = 0120644; + zipfi.external_fa = (_mode << 16L) | !(_mode & 0200); + zipfi.internal_fa = 0; + + zipOpenNewFileInZip4(p_zip, + p_folder.plus_file(f).utf8().get_data(), + &zipfi, + nullptr, + 0, + nullptr, + 0, + nullptr, + Z_DEFLATED, + Z_DEFAULT_COMPRESSION, + 0, + -MAX_WBITS, + DEF_MEM_LEVEL, + Z_DEFAULT_STRATEGY, + nullptr, + 0, + 0x0314, // "version made by", 0x03 - Unix, 0x14 - ZIP specification version 2.0, required to store Unix file permissions + 0); + + String target = da->read_link(f); + zipWriteInFileInZip(p_zip, target.utf8().get_data(), target.utf8().size()); + zipCloseFileInZip(p_zip); + } else if (da->current_is_dir()) { + _zip_folder_recursive(p_zip, p_root_path, p_folder.plus_file(f), p_pkg_name); + } else { + bool is_executable = (p_folder.ends_with("MacOS") && (f == p_pkg_name)); + + OS::Time time = OS::get_singleton()->get_time(); + OS::Date date = OS::get_singleton()->get_date(); + + zip_fileinfo zipfi; + zipfi.tmz_date.tm_hour = time.hour; + zipfi.tmz_date.tm_mday = date.day; + zipfi.tmz_date.tm_min = time.minute; + zipfi.tmz_date.tm_mon = date.month - 1; // Note: "tm" month range - 0..11, Godot month range - 1..12, http://www.cplusplus.com/reference/ctime/tm/ + zipfi.tmz_date.tm_sec = time.second; + zipfi.tmz_date.tm_year = date.year; + zipfi.dosDate = 0; + // 0100000: regular file type + // 0000755: permissions rwxr-xr-x + // 0000644: permissions rw-r--r-- + uint32_t _mode = (is_executable ? 0100755 : 0100644); + zipfi.external_fa = (_mode << 16L) | !(_mode & 0200); + zipfi.internal_fa = 0; + + zipOpenNewFileInZip4(p_zip, + p_folder.plus_file(f).utf8().get_data(), + &zipfi, + nullptr, + 0, + nullptr, + 0, + nullptr, + Z_DEFLATED, + Z_DEFAULT_COMPRESSION, + 0, + -MAX_WBITS, + DEF_MEM_LEVEL, + Z_DEFAULT_STRATEGY, + nullptr, + 0, + 0x0314, // "version made by", 0x03 - Unix, 0x14 - ZIP specification version 2.0, required to store Unix file permissions + 0); + + Vector<uint8_t> array = FileAccess::get_file_as_array(dir.plus_file(f)); + zipWriteInFileInZip(p_zip, array.ptr(), array.size()); + zipCloseFileInZip(p_zip); + } + } + da->list_dir_end(); + memdelete(da); +} + +bool EditorExportPlatformOSX::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const { + String err; + bool valid = false; + + // Look for export templates (first official, and if defined custom templates). + + bool dvalid = exists_export_template("osx.zip", &err); + bool rvalid = dvalid; // Both in the same ZIP. + + if (p_preset->get("custom_template/debug") != "") { + dvalid = FileAccess::exists(p_preset->get("custom_template/debug")); + if (!dvalid) { + err += TTR("Custom debug template not found.") + "\n"; + } + } + if (p_preset->get("custom_template/release") != "") { + rvalid = FileAccess::exists(p_preset->get("custom_template/release")); + if (!rvalid) { + err += TTR("Custom release template not found.") + "\n"; + } + } + + valid = dvalid || rvalid; + r_missing_templates = !valid; + + String identifier = p_preset->get("application/bundle_identifier"); + String pn_err; + if (!is_package_name_valid(identifier, &pn_err)) { + err += TTR("Invalid bundle identifier:") + " " + pn_err + "\n"; + valid = false; + } + + bool sign_enabled = p_preset->get("codesign/enable"); + bool noto_enabled = p_preset->get("notarization/enable"); + if (noto_enabled) { + if (!sign_enabled) { + err += TTR("Notarization: code signing required.") + "\n"; + valid = false; + } + bool hr_enabled = p_preset->get("codesign/hardened_runtime"); + if (!hr_enabled) { + err += TTR("Notarization: hardened runtime required.") + "\n"; + valid = false; + } + if (p_preset->get("notarization/apple_id_name") == "") { + err += TTR("Notarization: Apple ID name not specified.") + "\n"; + valid = false; + } + if (p_preset->get("notarization/apple_id_password") == "") { + err += TTR("Notarization: Apple ID password not specified.") + "\n"; + valid = false; + } + } + + if (!err.is_empty()) { + r_error = err; + } + return valid; +} + +EditorExportPlatformOSX::EditorExportPlatformOSX() { + Ref<Image> img = memnew(Image(_osx_logo)); + logo.instantiate(); + logo->create_from_image(img); +} + +EditorExportPlatformOSX::~EditorExportPlatformOSX() { +} diff --git a/platform/osx/export/export_plugin.h b/platform/osx/export/export_plugin.h new file mode 100644 index 0000000000..cd85ce2aad --- /dev/null +++ b/platform/osx/export/export_plugin.h @@ -0,0 +1,128 @@ +/*************************************************************************/ +/* export_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef OSX_EXPORT_PLUGIN_H +#define OSX_EXPORT_PLUGIN_H + +#include "core/config/project_settings.h" +#include "core/io/dir_access.h" +#include "core/io/file_access.h" +#include "core/io/marshalls.h" +#include "core/io/resource_saver.h" +#include "core/io/zip_io.h" +#include "core/os/os.h" +#include "core/version.h" +#include "editor/editor_export.h" +#include "editor/editor_node.h" +#include "editor/editor_settings.h" +#include "platform/osx/logo.gen.h" + +#include <sys/stat.h> + +class EditorExportPlatformOSX : public EditorExportPlatform { + GDCLASS(EditorExportPlatformOSX, EditorExportPlatform); + + int version_code = 0; + + Ref<ImageTexture> logo; + + void _fix_plist(const Ref<EditorExportPreset> &p_preset, Vector<uint8_t> &plist, const String &p_binary); + void _make_icon(const Ref<Image> &p_icon, Vector<uint8_t> &p_data); + + Error _notarize(const Ref<EditorExportPreset> &p_preset, const String &p_path); + Error _code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path, const String &p_ent_path); + Error _create_dmg(const String &p_dmg_path, const String &p_pkg_name, const String &p_app_path_name); + void _zip_folder_recursive(zipFile &p_zip, const String &p_root_path, const String &p_folder, const String &p_pkg_name); + +#ifdef OSX_ENABLED + bool use_codesign() const { return true; } + bool use_dmg() const { return true; } +#else + bool use_codesign() const { return false; } + bool use_dmg() const { return false; } +#endif + bool is_package_name_valid(const String &p_package, String *r_error = nullptr) const { + String pname = p_package; + + if (pname.length() == 0) { + if (r_error) { + *r_error = TTR("Identifier is missing."); + } + return false; + } + + for (int i = 0; i < pname.length(); i++) { + char32_t c = pname[i]; + if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-' || c == '.')) { + if (r_error) { + *r_error = vformat(TTR("The character '%s' is not allowed in Identifier."), String::chr(c)); + } + return false; + } + } + + return true; + } + +protected: + virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) override; + virtual void get_export_options(List<ExportOption> *r_options) override; + +public: + virtual String get_name() const override { return "macOS"; } + virtual String get_os_name() const override { return "macOS"; } + virtual Ref<Texture2D> get_logo() const override { return logo; } + + virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override { + List<String> list; + if (use_dmg()) { + list.push_back("dmg"); + } + list.push_back("zip"); + return list; + } + virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override; + + virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override; + + virtual void get_platform_features(List<String> *r_features) override { + r_features->push_back("pc"); + r_features->push_back("s3tc"); + r_features->push_back("macOS"); + } + + virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) override { + } + + EditorExportPlatformOSX(); + ~EditorExportPlatformOSX(); +}; + +#endif diff --git a/platform/uwp/export/app_packager.cpp b/platform/uwp/export/app_packager.cpp new file mode 100644 index 0000000000..39a2693f75 --- /dev/null +++ b/platform/uwp/export/app_packager.cpp @@ -0,0 +1,474 @@ +/*************************************************************************/ +/* app_packager.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "app_packager.h" + +String AppxPackager::hash_block(const uint8_t *p_block_data, size_t p_block_len) { + unsigned char hash[32]; + char base64[45]; + + CryptoCore::sha256(p_block_data, p_block_len, hash); + size_t len = 0; + CryptoCore::b64_encode((unsigned char *)base64, 45, &len, (unsigned char *)hash, 32); + base64[44] = '\0'; + + return String(base64); +} + +void AppxPackager::make_block_map(const String &p_path) { + FileAccess *tmp_file = FileAccess::open(p_path, FileAccess::WRITE); + + tmp_file->store_string("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"); + tmp_file->store_string("<BlockMap xmlns=\"http://schemas.microsoft.com/appx/2010/blockmap\" HashMethod=\"http://www.w3.org/2001/04/xmlenc#sha256\">"); + + for (int i = 0; i < file_metadata.size(); i++) { + FileMeta file = file_metadata[i]; + + tmp_file->store_string( + "<File Name=\"" + file.name.replace("/", "\\") + "\" Size=\"" + itos(file.uncompressed_size) + "\" LfhSize=\"" + itos(file.lfh_size) + "\">"); + + for (int j = 0; j < file.hashes.size(); j++) { + tmp_file->store_string("<Block Hash=\"" + file.hashes[j].base64_hash + "\" "); + if (file.compressed) { + tmp_file->store_string("Size=\"" + itos(file.hashes[j].compressed_size) + "\" "); + } + tmp_file->store_string("/>"); + } + + tmp_file->store_string("</File>"); + } + + tmp_file->store_string("</BlockMap>"); + + tmp_file->close(); + memdelete(tmp_file); +} + +String AppxPackager::content_type(String p_extension) { + if (p_extension == "png") { + return "image/png"; + } else if (p_extension == "jpg") { + return "image/jpg"; + } else if (p_extension == "xml") { + return "application/xml"; + } else if (p_extension == "exe" || p_extension == "dll") { + return "application/x-msdownload"; + } else { + return "application/octet-stream"; + } +} + +void AppxPackager::make_content_types(const String &p_path) { + FileAccess *tmp_file = FileAccess::open(p_path, FileAccess::WRITE); + + tmp_file->store_string("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); + tmp_file->store_string("<Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\">"); + + Map<String, String> types; + + for (int i = 0; i < file_metadata.size(); i++) { + String ext = file_metadata[i].name.get_extension().to_lower(); + + if (types.has(ext)) { + continue; + } + + types[ext] = content_type(ext); + + tmp_file->store_string("<Default Extension=\"" + ext + "\" ContentType=\"" + types[ext] + "\" />"); + } + + // Appx signature file + tmp_file->store_string("<Default Extension=\"p7x\" ContentType=\"application/octet-stream\" />"); + + // Override for package files + tmp_file->store_string("<Override PartName=\"/AppxManifest.xml\" ContentType=\"application/vnd.ms-appx.manifest+xml\" />"); + tmp_file->store_string("<Override PartName=\"/AppxBlockMap.xml\" ContentType=\"application/vnd.ms-appx.blockmap+xml\" />"); + tmp_file->store_string("<Override PartName=\"/AppxSignature.p7x\" ContentType=\"application/vnd.ms-appx.signature\" />"); + tmp_file->store_string("<Override PartName=\"/AppxMetadata/CodeIntegrity.cat\" ContentType=\"application/vnd.ms-pkiseccat\" />"); + + tmp_file->store_string("</Types>"); + + tmp_file->close(); + memdelete(tmp_file); +} + +Vector<uint8_t> AppxPackager::make_file_header(FileMeta p_file_meta) { + Vector<uint8_t> buf; + buf.resize(BASE_FILE_HEADER_SIZE + p_file_meta.name.length()); + + int offs = 0; + // Write magic + offs += buf_put_int32(FILE_HEADER_MAGIC, &buf.write[offs]); + + // Version + offs += buf_put_int16(ZIP_VERSION, &buf.write[offs]); + + // Special flag + offs += buf_put_int16(GENERAL_PURPOSE, &buf.write[offs]); + + // Compression + offs += buf_put_int16(p_file_meta.compressed ? Z_DEFLATED : 0, &buf.write[offs]); + + // File date and time + offs += buf_put_int32(0, &buf.write[offs]); + + // CRC-32 + offs += buf_put_int32(p_file_meta.file_crc32, &buf.write[offs]); + + // Compressed size + offs += buf_put_int32(p_file_meta.compressed_size, &buf.write[offs]); + + // Uncompressed size + offs += buf_put_int32(p_file_meta.uncompressed_size, &buf.write[offs]); + + // File name length + offs += buf_put_int16(p_file_meta.name.length(), &buf.write[offs]); + + // Extra data length + offs += buf_put_int16(0, &buf.write[offs]); + + // File name + offs += buf_put_string(p_file_meta.name, &buf.write[offs]); + + // Done! + return buf; +} + +void AppxPackager::store_central_dir_header(const FileMeta &p_file, bool p_do_hash) { + Vector<uint8_t> &buf = central_dir_data; + int offs = buf.size(); + buf.resize(buf.size() + BASE_CENTRAL_DIR_SIZE + p_file.name.length()); + + // Write magic + offs += buf_put_int32(CENTRAL_DIR_MAGIC, &buf.write[offs]); + + // ZIP versions + offs += buf_put_int16(ZIP_ARCHIVE_VERSION, &buf.write[offs]); + offs += buf_put_int16(ZIP_VERSION, &buf.write[offs]); + + // General purpose flag + offs += buf_put_int16(GENERAL_PURPOSE, &buf.write[offs]); + + // Compression + offs += buf_put_int16(p_file.compressed ? Z_DEFLATED : 0, &buf.write[offs]); + + // Modification date/time + offs += buf_put_int32(0, &buf.write[offs]); + + // Crc-32 + offs += buf_put_int32(p_file.file_crc32, &buf.write[offs]); + + // File sizes + offs += buf_put_int32(p_file.compressed_size, &buf.write[offs]); + offs += buf_put_int32(p_file.uncompressed_size, &buf.write[offs]); + + // File name length + offs += buf_put_int16(p_file.name.length(), &buf.write[offs]); + + // Extra field length + offs += buf_put_int16(0, &buf.write[offs]); + + // Comment length + offs += buf_put_int16(0, &buf.write[offs]); + + // Disk number start, internal/external file attributes + for (int i = 0; i < 8; i++) { + buf.write[offs++] = 0; + } + + // Relative offset + offs += buf_put_int32(p_file.zip_offset, &buf.write[offs]); + + // File name + offs += buf_put_string(p_file.name, &buf.write[offs]); + + // Done! +} + +Vector<uint8_t> AppxPackager::make_end_of_central_record() { + Vector<uint8_t> buf; + buf.resize(ZIP64_END_OF_CENTRAL_DIR_SIZE + 12 + END_OF_CENTRAL_DIR_SIZE); // Size plus magic + + int offs = 0; + + // Write magic + offs += buf_put_int32(ZIP64_END_OF_CENTRAL_DIR_MAGIC, &buf.write[offs]); + + // Size of this record + offs += buf_put_int64(ZIP64_END_OF_CENTRAL_DIR_SIZE, &buf.write[offs]); + + // Version (yes, twice) + offs += buf_put_int16(ZIP_ARCHIVE_VERSION, &buf.write[offs]); + offs += buf_put_int16(ZIP_ARCHIVE_VERSION, &buf.write[offs]); + + // Disk number + for (int i = 0; i < 8; i++) { + buf.write[offs++] = 0; + } + + // Number of entries (total and per disk) + offs += buf_put_int64(file_metadata.size(), &buf.write[offs]); + offs += buf_put_int64(file_metadata.size(), &buf.write[offs]); + + // Size of central dir + offs += buf_put_int64(central_dir_data.size(), &buf.write[offs]); + + // Central dir offset + offs += buf_put_int64(central_dir_offset, &buf.write[offs]); + + ////// ZIP64 locator + + // Write magic for zip64 central dir locator + offs += buf_put_int32(ZIP64_END_DIR_LOCATOR_MAGIC, &buf.write[offs]); + + // Disk number + for (int i = 0; i < 4; i++) { + buf.write[offs++] = 0; + } + + // Relative offset + offs += buf_put_int64(end_of_central_dir_offset, &buf.write[offs]); + + // Number of disks + offs += buf_put_int32(1, &buf.write[offs]); + + /////// End of zip directory + + // Write magic for end central dir + offs += buf_put_int32(END_OF_CENTRAL_DIR_MAGIC, &buf.write[offs]); + + // Dummy stuff for Zip64 + for (int i = 0; i < 4; i++) { + buf.write[offs++] = 0x0; + } + for (int i = 0; i < 12; i++) { + buf.write[offs++] = 0xFF; + } + + // Size of comments + for (int i = 0; i < 2; i++) { + buf.write[offs++] = 0; + } + + // Done! + return buf; +} + +void AppxPackager::init(FileAccess *p_fa) { + package = p_fa; + central_dir_offset = 0; + end_of_central_dir_offset = 0; +} + +Error AppxPackager::add_file(String p_file_name, const uint8_t *p_buffer, size_t p_len, int p_file_no, int p_total_files, bool p_compress) { + if (p_file_no >= 1 && p_total_files >= 1) { + if (EditorNode::progress_task_step(progress_task, "File: " + p_file_name, (p_file_no * 100) / p_total_files)) { + return ERR_SKIP; + } + } + + FileMeta meta; + meta.name = p_file_name; + meta.uncompressed_size = p_len; + meta.compressed_size = p_len; + meta.compressed = p_compress; + meta.zip_offset = package->get_position(); + + Vector<uint8_t> file_buffer; + + // Data for compression + z_stream strm; + FileAccess *strm_f = nullptr; + Vector<uint8_t> strm_in; + strm_in.resize(BLOCK_SIZE); + Vector<uint8_t> strm_out; + + if (p_compress) { + strm.zalloc = zipio_alloc; + strm.zfree = zipio_free; + strm.opaque = &strm_f; + + strm_out.resize(BLOCK_SIZE + 8); + + deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY); + } + + int step = 0; + + while (p_len - step > 0) { + size_t block_size = (p_len - step) > BLOCK_SIZE ? (size_t)BLOCK_SIZE : (p_len - step); + + for (uint64_t i = 0; i < block_size; i++) { + strm_in.write[i] = p_buffer[step + i]; + } + + BlockHash bh; + bh.base64_hash = hash_block(strm_in.ptr(), block_size); + + if (p_compress) { + strm.avail_in = block_size; + strm.avail_out = strm_out.size(); + strm.next_in = (uint8_t *)strm_in.ptr(); + strm.next_out = strm_out.ptrw(); + + int total_out_before = strm.total_out; + + int err = deflate(&strm, Z_FULL_FLUSH); + ERR_FAIL_COND_V(err < 0, ERR_BUG); // Negative means bug + + bh.compressed_size = strm.total_out - total_out_before; + + //package->store_buffer(strm_out.ptr(), strm.total_out - total_out_before); + int start = file_buffer.size(); + file_buffer.resize(file_buffer.size() + bh.compressed_size); + for (uint64_t i = 0; i < bh.compressed_size; i++) { + file_buffer.write[start + i] = strm_out[i]; + } + } else { + bh.compressed_size = block_size; + //package->store_buffer(strm_in.ptr(), block_size); + int start = file_buffer.size(); + file_buffer.resize(file_buffer.size() + block_size); + for (uint64_t i = 0; i < bh.compressed_size; i++) { + file_buffer.write[start + i] = strm_in[i]; + } + } + + meta.hashes.push_back(bh); + + step += block_size; + } + + if (p_compress) { + strm.avail_in = 0; + strm.avail_out = strm_out.size(); + strm.next_in = (uint8_t *)strm_in.ptr(); + strm.next_out = strm_out.ptrw(); + + int total_out_before = strm.total_out; + + deflate(&strm, Z_FINISH); + + //package->store_buffer(strm_out.ptr(), strm.total_out - total_out_before); + int start = file_buffer.size(); + file_buffer.resize(file_buffer.size() + (strm.total_out - total_out_before)); + for (uint64_t i = 0; i < (strm.total_out - total_out_before); i++) { + file_buffer.write[start + i] = strm_out[i]; + } + + deflateEnd(&strm); + meta.compressed_size = strm.total_out; + + } else { + meta.compressed_size = p_len; + } + + // Calculate file CRC-32 + uLong crc = crc32(0L, Z_NULL, 0); + crc = crc32(crc, p_buffer, p_len); + meta.file_crc32 = crc; + + // Create file header + Vector<uint8_t> file_header = make_file_header(meta); + meta.lfh_size = file_header.size(); + + // Store the header and file; + package->store_buffer(file_header.ptr(), file_header.size()); + package->store_buffer(file_buffer.ptr(), file_buffer.size()); + + file_metadata.push_back(meta); + + return OK; +} + +void AppxPackager::finish() { + // Create and add block map file + EditorNode::progress_task_step("export", "Creating block map...", 4); + + const String &tmp_blockmap_file_path = EditorPaths::get_singleton()->get_cache_dir().plus_file("tmpblockmap.xml"); + make_block_map(tmp_blockmap_file_path); + + FileAccess *blockmap_file = FileAccess::open(tmp_blockmap_file_path, FileAccess::READ); + Vector<uint8_t> blockmap_buffer; + blockmap_buffer.resize(blockmap_file->get_length()); + + blockmap_file->get_buffer(blockmap_buffer.ptrw(), blockmap_buffer.size()); + + add_file("AppxBlockMap.xml", blockmap_buffer.ptr(), blockmap_buffer.size(), -1, -1, true); + + blockmap_file->close(); + memdelete(blockmap_file); + + // Add content types + + EditorNode::progress_task_step("export", "Setting content types...", 5); + + const String &tmp_content_types_file_path = EditorPaths::get_singleton()->get_cache_dir().plus_file("tmpcontenttypes.xml"); + make_content_types(tmp_content_types_file_path); + + FileAccess *types_file = FileAccess::open(tmp_content_types_file_path, FileAccess::READ); + Vector<uint8_t> types_buffer; + types_buffer.resize(types_file->get_length()); + + types_file->get_buffer(types_buffer.ptrw(), types_buffer.size()); + + add_file("[Content_Types].xml", types_buffer.ptr(), types_buffer.size(), -1, -1, true); + + types_file->close(); + memdelete(types_file); + + // Cleanup generated files. + DirAccess::remove_file_or_error(tmp_blockmap_file_path); + DirAccess::remove_file_or_error(tmp_content_types_file_path); + + // Pre-process central directory before signing + for (int i = 0; i < file_metadata.size(); i++) { + store_central_dir_header(file_metadata[i]); + } + + // Write central directory + EditorNode::progress_task_step("export", "Finishing package...", 6); + central_dir_offset = package->get_position(); + package->store_buffer(central_dir_data.ptr(), central_dir_data.size()); + + // End record + end_of_central_dir_offset = package->get_position(); + Vector<uint8_t> end_record = make_end_of_central_record(); + package->store_buffer(end_record.ptr(), end_record.size()); + + package->close(); + memdelete(package); + package = nullptr; +} + +AppxPackager::AppxPackager() {} + +AppxPackager::~AppxPackager() {} diff --git a/platform/uwp/export/app_packager.h b/platform/uwp/export/app_packager.h new file mode 100644 index 0000000000..0eb1a78df1 --- /dev/null +++ b/platform/uwp/export/app_packager.h @@ -0,0 +1,150 @@ +/*************************************************************************/ +/* app_packager.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef UWP_APP_PACKAGER_H +#define UWP_APP_PACKAGER_H + +#include "core/config/project_settings.h" +#include "core/core_bind.h" +#include "core/crypto/crypto_core.h" +#include "core/io/dir_access.h" +#include "core/io/file_access.h" +#include "core/io/marshalls.h" +#include "core/io/zip_io.h" +#include "core/object/class_db.h" +#include "core/version.h" +#include "editor/editor_export.h" +#include "editor/editor_node.h" + +#include "thirdparty/minizip/unzip.h" +#include "thirdparty/minizip/zip.h" + +#include <zlib.h> + +class AppxPackager { + enum { + FILE_HEADER_MAGIC = 0x04034b50, + DATA_DESCRIPTOR_MAGIC = 0x08074b50, + CENTRAL_DIR_MAGIC = 0x02014b50, + END_OF_CENTRAL_DIR_MAGIC = 0x06054b50, + ZIP64_END_OF_CENTRAL_DIR_MAGIC = 0x06064b50, + ZIP64_END_DIR_LOCATOR_MAGIC = 0x07064b50, + P7X_SIGNATURE = 0x58434b50, + ZIP64_HEADER_ID = 0x0001, + ZIP_VERSION = 20, + ZIP_ARCHIVE_VERSION = 45, + GENERAL_PURPOSE = 0x00, + BASE_FILE_HEADER_SIZE = 30, + DATA_DESCRIPTOR_SIZE = 24, + BASE_CENTRAL_DIR_SIZE = 46, + EXTRA_FIELD_LENGTH = 28, + ZIP64_HEADER_SIZE = 24, + ZIP64_END_OF_CENTRAL_DIR_SIZE = (56 - 12), + END_OF_CENTRAL_DIR_SIZE = 42, + BLOCK_SIZE = 65536, + }; + + struct BlockHash { + String base64_hash; + size_t compressed_size = 0; + }; + + struct FileMeta { + String name; + int lfh_size = 0; + bool compressed = false; + size_t compressed_size = 0; + size_t uncompressed_size = 0; + Vector<BlockHash> hashes; + uLong file_crc32 = 0; + ZPOS64_T zip_offset = 0; + }; + + String progress_task; + FileAccess *package = nullptr; + + Set<String> mime_types; + + Vector<FileMeta> file_metadata; + + ZPOS64_T central_dir_offset; + ZPOS64_T end_of_central_dir_offset; + Vector<uint8_t> central_dir_data; + + String hash_block(const uint8_t *p_block_data, size_t p_block_len); + + void make_block_map(const String &p_path); + void make_content_types(const String &p_path); + + _FORCE_INLINE_ unsigned int buf_put_int16(uint16_t p_val, uint8_t *p_buf) { + for (int i = 0; i < 2; i++) { + *p_buf++ = (p_val >> (i * 8)) & 0xFF; + } + return 2; + } + + _FORCE_INLINE_ unsigned int buf_put_int32(uint32_t p_val, uint8_t *p_buf) { + for (int i = 0; i < 4; i++) { + *p_buf++ = (p_val >> (i * 8)) & 0xFF; + } + return 4; + } + + _FORCE_INLINE_ unsigned int buf_put_int64(uint64_t p_val, uint8_t *p_buf) { + for (int i = 0; i < 8; i++) { + *p_buf++ = (p_val >> (i * 8)) & 0xFF; + } + return 8; + } + + _FORCE_INLINE_ unsigned int buf_put_string(String p_val, uint8_t *p_buf) { + for (int i = 0; i < p_val.length(); i++) { + *p_buf++ = p_val.utf8().get(i); + } + return p_val.length(); + } + + Vector<uint8_t> make_file_header(FileMeta p_file_meta); + void store_central_dir_header(const FileMeta &p_file, bool p_do_hash = true); + Vector<uint8_t> make_end_of_central_record(); + + String content_type(String p_extension); + +public: + void set_progress_task(String p_task) { progress_task = p_task; } + void init(FileAccess *p_fa); + Error add_file(String p_file_name, const uint8_t *p_buffer, size_t p_len, int p_file_no, int p_total_files, bool p_compress = false); + void finish(); + + AppxPackager(); + ~AppxPackager(); +}; + +#endif diff --git a/platform/uwp/export/export.cpp b/platform/uwp/export/export.cpp index 98925fd7fa..f5c3db33bb 100644 --- a/platform/uwp/export/export.cpp +++ b/platform/uwp/export/export.cpp @@ -30,1408 +30,7 @@ #include "export.h" -#include "core/config/project_settings.h" -#include "core/crypto/crypto_core.h" -#include "core/io/dir_access.h" -#include "core/io/file_access.h" -#include "core/io/marshalls.h" -#include "core/io/zip_io.h" -#include "core/object/class_db.h" -#include "core/version.h" -#include "editor/editor_export.h" -#include "editor/editor_node.h" -#include "platform/uwp/logo.gen.h" - -#include "thirdparty/minizip/unzip.h" -#include "thirdparty/minizip/zip.h" - -#include <zlib.h> - -// Capabilities -static const char *uwp_capabilities[] = { - "allJoyn", - "codeGeneration", - "internetClient", - "internetClientServer", - "privateNetworkClientServer", - nullptr -}; -static const char *uwp_uap_capabilities[] = { - "appointments", - "blockedChatMessages", - "chat", - "contacts", - "enterpriseAuthentication", - "musicLibrary", - "objects3D", - "picturesLibrary", - "phoneCall", - "removableStorage", - "sharedUserCertificates", - "userAccountInformation", - "videosLibrary", - "voipCall", - nullptr -}; -static const char *uwp_device_capabilities[] = { - "bluetooth", - "location", - "microphone", - "proximity", - "webcam", - nullptr -}; - -class AppxPackager { - enum { - FILE_HEADER_MAGIC = 0x04034b50, - DATA_DESCRIPTOR_MAGIC = 0x08074b50, - CENTRAL_DIR_MAGIC = 0x02014b50, - END_OF_CENTRAL_DIR_MAGIC = 0x06054b50, - ZIP64_END_OF_CENTRAL_DIR_MAGIC = 0x06064b50, - ZIP64_END_DIR_LOCATOR_MAGIC = 0x07064b50, - P7X_SIGNATURE = 0x58434b50, - ZIP64_HEADER_ID = 0x0001, - ZIP_VERSION = 20, - ZIP_ARCHIVE_VERSION = 45, - GENERAL_PURPOSE = 0x00, - BASE_FILE_HEADER_SIZE = 30, - DATA_DESCRIPTOR_SIZE = 24, - BASE_CENTRAL_DIR_SIZE = 46, - EXTRA_FIELD_LENGTH = 28, - ZIP64_HEADER_SIZE = 24, - ZIP64_END_OF_CENTRAL_DIR_SIZE = (56 - 12), - END_OF_CENTRAL_DIR_SIZE = 42, - BLOCK_SIZE = 65536, - }; - - struct BlockHash { - String base64_hash; - size_t compressed_size = 0; - }; - - struct FileMeta { - String name; - int lfh_size = 0; - bool compressed = false; - size_t compressed_size = 0; - size_t uncompressed_size = 0; - Vector<BlockHash> hashes; - uLong file_crc32 = 0; - ZPOS64_T zip_offset = 0; - }; - - String progress_task; - FileAccess *package = nullptr; - - Set<String> mime_types; - - Vector<FileMeta> file_metadata; - - ZPOS64_T central_dir_offset; - ZPOS64_T end_of_central_dir_offset; - Vector<uint8_t> central_dir_data; - - String hash_block(const uint8_t *p_block_data, size_t p_block_len); - - void make_block_map(const String &p_path); - void make_content_types(const String &p_path); - - _FORCE_INLINE_ unsigned int buf_put_int16(uint16_t p_val, uint8_t *p_buf) { - for (int i = 0; i < 2; i++) { - *p_buf++ = (p_val >> (i * 8)) & 0xFF; - } - return 2; - } - - _FORCE_INLINE_ unsigned int buf_put_int32(uint32_t p_val, uint8_t *p_buf) { - for (int i = 0; i < 4; i++) { - *p_buf++ = (p_val >> (i * 8)) & 0xFF; - } - return 4; - } - - _FORCE_INLINE_ unsigned int buf_put_int64(uint64_t p_val, uint8_t *p_buf) { - for (int i = 0; i < 8; i++) { - *p_buf++ = (p_val >> (i * 8)) & 0xFF; - } - return 8; - } - - _FORCE_INLINE_ unsigned int buf_put_string(String p_val, uint8_t *p_buf) { - for (int i = 0; i < p_val.length(); i++) { - *p_buf++ = p_val.utf8().get(i); - } - return p_val.length(); - } - - Vector<uint8_t> make_file_header(FileMeta p_file_meta); - void store_central_dir_header(const FileMeta &p_file, bool p_do_hash = true); - Vector<uint8_t> make_end_of_central_record(); - - String content_type(String p_extension); - -public: - void set_progress_task(String p_task) { progress_task = p_task; } - void init(FileAccess *p_fa); - Error add_file(String p_file_name, const uint8_t *p_buffer, size_t p_len, int p_file_no, int p_total_files, bool p_compress = false); - void finish(); - - AppxPackager(); - ~AppxPackager(); -}; - -/////////////////////////////////////////////////////////////////////////// - -String AppxPackager::hash_block(const uint8_t *p_block_data, size_t p_block_len) { - unsigned char hash[32]; - char base64[45]; - - CryptoCore::sha256(p_block_data, p_block_len, hash); - size_t len = 0; - CryptoCore::b64_encode((unsigned char *)base64, 45, &len, (unsigned char *)hash, 32); - base64[44] = '\0'; - - return String(base64); -} - -void AppxPackager::make_block_map(const String &p_path) { - FileAccess *tmp_file = FileAccess::open(p_path, FileAccess::WRITE); - - tmp_file->store_string("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"); - tmp_file->store_string("<BlockMap xmlns=\"http://schemas.microsoft.com/appx/2010/blockmap\" HashMethod=\"http://www.w3.org/2001/04/xmlenc#sha256\">"); - - for (int i = 0; i < file_metadata.size(); i++) { - FileMeta file = file_metadata[i]; - - tmp_file->store_string( - "<File Name=\"" + file.name.replace("/", "\\") + "\" Size=\"" + itos(file.uncompressed_size) + "\" LfhSize=\"" + itos(file.lfh_size) + "\">"); - - for (int j = 0; j < file.hashes.size(); j++) { - tmp_file->store_string("<Block Hash=\"" + file.hashes[j].base64_hash + "\" "); - if (file.compressed) { - tmp_file->store_string("Size=\"" + itos(file.hashes[j].compressed_size) + "\" "); - } - tmp_file->store_string("/>"); - } - - tmp_file->store_string("</File>"); - } - - tmp_file->store_string("</BlockMap>"); - - tmp_file->close(); - memdelete(tmp_file); -} - -String AppxPackager::content_type(String p_extension) { - if (p_extension == "png") { - return "image/png"; - } else if (p_extension == "jpg") { - return "image/jpg"; - } else if (p_extension == "xml") { - return "application/xml"; - } else if (p_extension == "exe" || p_extension == "dll") { - return "application/x-msdownload"; - } else { - return "application/octet-stream"; - } -} - -void AppxPackager::make_content_types(const String &p_path) { - FileAccess *tmp_file = FileAccess::open(p_path, FileAccess::WRITE); - - tmp_file->store_string("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); - tmp_file->store_string("<Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\">"); - - Map<String, String> types; - - for (int i = 0; i < file_metadata.size(); i++) { - String ext = file_metadata[i].name.get_extension().to_lower(); - - if (types.has(ext)) { - continue; - } - - types[ext] = content_type(ext); - - tmp_file->store_string("<Default Extension=\"" + ext + "\" ContentType=\"" + types[ext] + "\" />"); - } - - // Appx signature file - tmp_file->store_string("<Default Extension=\"p7x\" ContentType=\"application/octet-stream\" />"); - - // Override for package files - tmp_file->store_string("<Override PartName=\"/AppxManifest.xml\" ContentType=\"application/vnd.ms-appx.manifest+xml\" />"); - tmp_file->store_string("<Override PartName=\"/AppxBlockMap.xml\" ContentType=\"application/vnd.ms-appx.blockmap+xml\" />"); - tmp_file->store_string("<Override PartName=\"/AppxSignature.p7x\" ContentType=\"application/vnd.ms-appx.signature\" />"); - tmp_file->store_string("<Override PartName=\"/AppxMetadata/CodeIntegrity.cat\" ContentType=\"application/vnd.ms-pkiseccat\" />"); - - tmp_file->store_string("</Types>"); - - tmp_file->close(); - memdelete(tmp_file); -} - -Vector<uint8_t> AppxPackager::make_file_header(FileMeta p_file_meta) { - Vector<uint8_t> buf; - buf.resize(BASE_FILE_HEADER_SIZE + p_file_meta.name.length()); - - int offs = 0; - // Write magic - offs += buf_put_int32(FILE_HEADER_MAGIC, &buf.write[offs]); - - // Version - offs += buf_put_int16(ZIP_VERSION, &buf.write[offs]); - - // Special flag - offs += buf_put_int16(GENERAL_PURPOSE, &buf.write[offs]); - - // Compression - offs += buf_put_int16(p_file_meta.compressed ? Z_DEFLATED : 0, &buf.write[offs]); - - // File date and time - offs += buf_put_int32(0, &buf.write[offs]); - - // CRC-32 - offs += buf_put_int32(p_file_meta.file_crc32, &buf.write[offs]); - - // Compressed size - offs += buf_put_int32(p_file_meta.compressed_size, &buf.write[offs]); - - // Uncompressed size - offs += buf_put_int32(p_file_meta.uncompressed_size, &buf.write[offs]); - - // File name length - offs += buf_put_int16(p_file_meta.name.length(), &buf.write[offs]); - - // Extra data length - offs += buf_put_int16(0, &buf.write[offs]); - - // File name - offs += buf_put_string(p_file_meta.name, &buf.write[offs]); - - // Done! - return buf; -} - -void AppxPackager::store_central_dir_header(const FileMeta &p_file, bool p_do_hash) { - Vector<uint8_t> &buf = central_dir_data; - int offs = buf.size(); - buf.resize(buf.size() + BASE_CENTRAL_DIR_SIZE + p_file.name.length()); - - // Write magic - offs += buf_put_int32(CENTRAL_DIR_MAGIC, &buf.write[offs]); - - // ZIP versions - offs += buf_put_int16(ZIP_ARCHIVE_VERSION, &buf.write[offs]); - offs += buf_put_int16(ZIP_VERSION, &buf.write[offs]); - - // General purpose flag - offs += buf_put_int16(GENERAL_PURPOSE, &buf.write[offs]); - - // Compression - offs += buf_put_int16(p_file.compressed ? Z_DEFLATED : 0, &buf.write[offs]); - - // Modification date/time - offs += buf_put_int32(0, &buf.write[offs]); - - // Crc-32 - offs += buf_put_int32(p_file.file_crc32, &buf.write[offs]); - - // File sizes - offs += buf_put_int32(p_file.compressed_size, &buf.write[offs]); - offs += buf_put_int32(p_file.uncompressed_size, &buf.write[offs]); - - // File name length - offs += buf_put_int16(p_file.name.length(), &buf.write[offs]); - - // Extra field length - offs += buf_put_int16(0, &buf.write[offs]); - - // Comment length - offs += buf_put_int16(0, &buf.write[offs]); - - // Disk number start, internal/external file attributes - for (int i = 0; i < 8; i++) { - buf.write[offs++] = 0; - } - - // Relative offset - offs += buf_put_int32(p_file.zip_offset, &buf.write[offs]); - - // File name - offs += buf_put_string(p_file.name, &buf.write[offs]); - - // Done! -} - -Vector<uint8_t> AppxPackager::make_end_of_central_record() { - Vector<uint8_t> buf; - buf.resize(ZIP64_END_OF_CENTRAL_DIR_SIZE + 12 + END_OF_CENTRAL_DIR_SIZE); // Size plus magic - - int offs = 0; - - // Write magic - offs += buf_put_int32(ZIP64_END_OF_CENTRAL_DIR_MAGIC, &buf.write[offs]); - - // Size of this record - offs += buf_put_int64(ZIP64_END_OF_CENTRAL_DIR_SIZE, &buf.write[offs]); - - // Version (yes, twice) - offs += buf_put_int16(ZIP_ARCHIVE_VERSION, &buf.write[offs]); - offs += buf_put_int16(ZIP_ARCHIVE_VERSION, &buf.write[offs]); - - // Disk number - for (int i = 0; i < 8; i++) { - buf.write[offs++] = 0; - } - - // Number of entries (total and per disk) - offs += buf_put_int64(file_metadata.size(), &buf.write[offs]); - offs += buf_put_int64(file_metadata.size(), &buf.write[offs]); - - // Size of central dir - offs += buf_put_int64(central_dir_data.size(), &buf.write[offs]); - - // Central dir offset - offs += buf_put_int64(central_dir_offset, &buf.write[offs]); - - ////// ZIP64 locator - - // Write magic for zip64 central dir locator - offs += buf_put_int32(ZIP64_END_DIR_LOCATOR_MAGIC, &buf.write[offs]); - - // Disk number - for (int i = 0; i < 4; i++) { - buf.write[offs++] = 0; - } - - // Relative offset - offs += buf_put_int64(end_of_central_dir_offset, &buf.write[offs]); - - // Number of disks - offs += buf_put_int32(1, &buf.write[offs]); - - /////// End of zip directory - - // Write magic for end central dir - offs += buf_put_int32(END_OF_CENTRAL_DIR_MAGIC, &buf.write[offs]); - - // Dummy stuff for Zip64 - for (int i = 0; i < 4; i++) { - buf.write[offs++] = 0x0; - } - for (int i = 0; i < 12; i++) { - buf.write[offs++] = 0xFF; - } - - // Size of comments - for (int i = 0; i < 2; i++) { - buf.write[offs++] = 0; - } - - // Done! - return buf; -} - -void AppxPackager::init(FileAccess *p_fa) { - package = p_fa; - central_dir_offset = 0; - end_of_central_dir_offset = 0; -} - -Error AppxPackager::add_file(String p_file_name, const uint8_t *p_buffer, size_t p_len, int p_file_no, int p_total_files, bool p_compress) { - if (p_file_no >= 1 && p_total_files >= 1) { - if (EditorNode::progress_task_step(progress_task, "File: " + p_file_name, (p_file_no * 100) / p_total_files)) { - return ERR_SKIP; - } - } - - FileMeta meta; - meta.name = p_file_name; - meta.uncompressed_size = p_len; - meta.compressed_size = p_len; - meta.compressed = p_compress; - meta.zip_offset = package->get_position(); - - Vector<uint8_t> file_buffer; - - // Data for compression - z_stream strm; - FileAccess *strm_f = nullptr; - Vector<uint8_t> strm_in; - strm_in.resize(BLOCK_SIZE); - Vector<uint8_t> strm_out; - - if (p_compress) { - strm.zalloc = zipio_alloc; - strm.zfree = zipio_free; - strm.opaque = &strm_f; - - strm_out.resize(BLOCK_SIZE + 8); - - deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY); - } - - int step = 0; - - while (p_len - step > 0) { - size_t block_size = (p_len - step) > BLOCK_SIZE ? (size_t)BLOCK_SIZE : (p_len - step); - - for (uint64_t i = 0; i < block_size; i++) { - strm_in.write[i] = p_buffer[step + i]; - } - - BlockHash bh; - bh.base64_hash = hash_block(strm_in.ptr(), block_size); - - if (p_compress) { - strm.avail_in = block_size; - strm.avail_out = strm_out.size(); - strm.next_in = (uint8_t *)strm_in.ptr(); - strm.next_out = strm_out.ptrw(); - - int total_out_before = strm.total_out; - - int err = deflate(&strm, Z_FULL_FLUSH); - ERR_FAIL_COND_V(err < 0, ERR_BUG); // Negative means bug - - bh.compressed_size = strm.total_out - total_out_before; - - //package->store_buffer(strm_out.ptr(), strm.total_out - total_out_before); - int start = file_buffer.size(); - file_buffer.resize(file_buffer.size() + bh.compressed_size); - for (uint64_t i = 0; i < bh.compressed_size; i++) { - file_buffer.write[start + i] = strm_out[i]; - } - } else { - bh.compressed_size = block_size; - //package->store_buffer(strm_in.ptr(), block_size); - int start = file_buffer.size(); - file_buffer.resize(file_buffer.size() + block_size); - for (uint64_t i = 0; i < bh.compressed_size; i++) { - file_buffer.write[start + i] = strm_in[i]; - } - } - - meta.hashes.push_back(bh); - - step += block_size; - } - - if (p_compress) { - strm.avail_in = 0; - strm.avail_out = strm_out.size(); - strm.next_in = (uint8_t *)strm_in.ptr(); - strm.next_out = strm_out.ptrw(); - - int total_out_before = strm.total_out; - - deflate(&strm, Z_FINISH); - - //package->store_buffer(strm_out.ptr(), strm.total_out - total_out_before); - int start = file_buffer.size(); - file_buffer.resize(file_buffer.size() + (strm.total_out - total_out_before)); - for (uint64_t i = 0; i < (strm.total_out - total_out_before); i++) { - file_buffer.write[start + i] = strm_out[i]; - } - - deflateEnd(&strm); - meta.compressed_size = strm.total_out; - - } else { - meta.compressed_size = p_len; - } - - // Calculate file CRC-32 - uLong crc = crc32(0L, Z_NULL, 0); - crc = crc32(crc, p_buffer, p_len); - meta.file_crc32 = crc; - - // Create file header - Vector<uint8_t> file_header = make_file_header(meta); - meta.lfh_size = file_header.size(); - - // Store the header and file; - package->store_buffer(file_header.ptr(), file_header.size()); - package->store_buffer(file_buffer.ptr(), file_buffer.size()); - - file_metadata.push_back(meta); - - return OK; -} - -void AppxPackager::finish() { - // Create and add block map file - EditorNode::progress_task_step("export", "Creating block map...", 4); - - const String &tmp_blockmap_file_path = EditorPaths::get_singleton()->get_cache_dir().plus_file("tmpblockmap.xml"); - make_block_map(tmp_blockmap_file_path); - - FileAccess *blockmap_file = FileAccess::open(tmp_blockmap_file_path, FileAccess::READ); - Vector<uint8_t> blockmap_buffer; - blockmap_buffer.resize(blockmap_file->get_length()); - - blockmap_file->get_buffer(blockmap_buffer.ptrw(), blockmap_buffer.size()); - - add_file("AppxBlockMap.xml", blockmap_buffer.ptr(), blockmap_buffer.size(), -1, -1, true); - - blockmap_file->close(); - memdelete(blockmap_file); - - // Add content types - - EditorNode::progress_task_step("export", "Setting content types...", 5); - - const String &tmp_content_types_file_path = EditorPaths::get_singleton()->get_cache_dir().plus_file("tmpcontenttypes.xml"); - make_content_types(tmp_content_types_file_path); - - FileAccess *types_file = FileAccess::open(tmp_content_types_file_path, FileAccess::READ); - Vector<uint8_t> types_buffer; - types_buffer.resize(types_file->get_length()); - - types_file->get_buffer(types_buffer.ptrw(), types_buffer.size()); - - add_file("[Content_Types].xml", types_buffer.ptr(), types_buffer.size(), -1, -1, true); - - types_file->close(); - memdelete(types_file); - - // Cleanup generated files. - DirAccess::remove_file_or_error(tmp_blockmap_file_path); - DirAccess::remove_file_or_error(tmp_content_types_file_path); - - // Pre-process central directory before signing - for (int i = 0; i < file_metadata.size(); i++) { - store_central_dir_header(file_metadata[i]); - } - - // Write central directory - EditorNode::progress_task_step("export", "Finishing package...", 6); - central_dir_offset = package->get_position(); - package->store_buffer(central_dir_data.ptr(), central_dir_data.size()); - - // End record - end_of_central_dir_offset = package->get_position(); - Vector<uint8_t> end_record = make_end_of_central_record(); - package->store_buffer(end_record.ptr(), end_record.size()); - - package->close(); - memdelete(package); - package = nullptr; -} - -AppxPackager::AppxPackager() {} - -AppxPackager::~AppxPackager() {} - -//////////////////////////////////////////////////////////////////// - -class EditorExportPlatformUWP : public EditorExportPlatform { - GDCLASS(EditorExportPlatformUWP, EditorExportPlatform); - - Ref<ImageTexture> logo; - - enum Platform { - ARM, - X86, - X64 - }; - - bool _valid_resource_name(const String &p_name) const { - if (p_name.is_empty()) { - return false; - } - if (p_name.ends_with(".")) { - return false; - } - - static const char *invalid_names[] = { - "CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", - "COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9", - nullptr - }; - - const char **t = invalid_names; - while (*t) { - if (p_name == *t) { - return false; - } - t++; - } - - return true; - } - - bool _valid_guid(const String &p_guid) const { - Vector<String> parts = p_guid.split("-"); - - if (parts.size() != 5) { - return false; - } - if (parts[0].length() != 8) { - return false; - } - for (int i = 1; i < 4; i++) { - if (parts[i].length() != 4) { - return false; - } - } - if (parts[4].length() != 12) { - return false; - } - - return true; - } - - bool _valid_bgcolor(const String &p_color) const { - if (p_color.is_empty()) { - return true; - } - if (p_color.begins_with("#") && p_color.is_valid_html_color()) { - return true; - } - - // Colors from https://msdn.microsoft.com/en-us/library/windows/apps/dn934817.aspx - static const char *valid_colors[] = { - "aliceBlue", "antiqueWhite", "aqua", "aquamarine", "azure", "beige", - "bisque", "black", "blanchedAlmond", "blue", "blueViolet", "brown", - "burlyWood", "cadetBlue", "chartreuse", "chocolate", "coral", "cornflowerBlue", - "cornsilk", "crimson", "cyan", "darkBlue", "darkCyan", "darkGoldenrod", - "darkGray", "darkGreen", "darkKhaki", "darkMagenta", "darkOliveGreen", "darkOrange", - "darkOrchid", "darkRed", "darkSalmon", "darkSeaGreen", "darkSlateBlue", "darkSlateGray", - "darkTurquoise", "darkViolet", "deepPink", "deepSkyBlue", "dimGray", "dodgerBlue", - "firebrick", "floralWhite", "forestGreen", "fuchsia", "gainsboro", "ghostWhite", - "gold", "goldenrod", "gray", "green", "greenYellow", "honeydew", - "hotPink", "indianRed", "indigo", "ivory", "khaki", "lavender", - "lavenderBlush", "lawnGreen", "lemonChiffon", "lightBlue", "lightCoral", "lightCyan", - "lightGoldenrodYellow", "lightGreen", "lightGray", "lightPink", "lightSalmon", "lightSeaGreen", - "lightSkyBlue", "lightSlateGray", "lightSteelBlue", "lightYellow", "lime", "limeGreen", - "linen", "magenta", "maroon", "mediumAquamarine", "mediumBlue", "mediumOrchid", - "mediumPurple", "mediumSeaGreen", "mediumSlateBlue", "mediumSpringGreen", "mediumTurquoise", "mediumVioletRed", - "midnightBlue", "mintCream", "mistyRose", "moccasin", "navajoWhite", "navy", - "oldLace", "olive", "oliveDrab", "orange", "orangeRed", "orchid", - "paleGoldenrod", "paleGreen", "paleTurquoise", "paleVioletRed", "papayaWhip", "peachPuff", - "peru", "pink", "plum", "powderBlue", "purple", "red", - "rosyBrown", "royalBlue", "saddleBrown", "salmon", "sandyBrown", "seaGreen", - "seaShell", "sienna", "silver", "skyBlue", "slateBlue", "slateGray", - "snow", "springGreen", "steelBlue", "tan", "teal", "thistle", - "tomato", "transparent", "turquoise", "violet", "wheat", "white", - "whiteSmoke", "yellow", "yellowGreen", - nullptr - }; - - const char **color = valid_colors; - - while (*color) { - if (p_color == *color) { - return true; - } - color++; - } - - return false; - } - - bool _valid_image(const StreamTexture2D *p_image, int p_width, int p_height) const { - if (!p_image) { - return false; - } - - // TODO: Add resource creation or image rescaling to enable other scales: - // 1.25, 1.5, 2.0 - return p_width == p_image->get_width() && p_height == p_image->get_height(); - } - - Vector<uint8_t> _fix_manifest(const Ref<EditorExportPreset> &p_preset, const Vector<uint8_t> &p_template, bool p_give_internet) const { - String result = String::utf8((const char *)p_template.ptr(), p_template.size()); - - result = result.replace("$godot_version$", VERSION_FULL_NAME); - - result = result.replace("$identity_name$", p_preset->get("package/unique_name")); - result = result.replace("$publisher$", p_preset->get("package/publisher")); - - result = result.replace("$product_guid$", p_preset->get("identity/product_guid")); - result = result.replace("$publisher_guid$", p_preset->get("identity/publisher_guid")); - - String version = itos(p_preset->get("version/major")) + "." + itos(p_preset->get("version/minor")) + "." + itos(p_preset->get("version/build")) + "." + itos(p_preset->get("version/revision")); - result = result.replace("$version_string$", version); - - Platform arch = (Platform)(int)p_preset->get("architecture/target"); - String architecture = arch == ARM ? "arm" : (arch == X86 ? "x86" : "x64"); - result = result.replace("$architecture$", architecture); - - result = result.replace("$display_name$", String(p_preset->get("package/display_name")).is_empty() ? (String)ProjectSettings::get_singleton()->get("application/config/name") : String(p_preset->get("package/display_name"))); - - result = result.replace("$publisher_display_name$", p_preset->get("package/publisher_display_name")); - result = result.replace("$app_description$", p_preset->get("package/description")); - result = result.replace("$bg_color$", p_preset->get("images/background_color")); - result = result.replace("$short_name$", p_preset->get("package/short_name")); - - String name_on_tiles = ""; - if ((bool)p_preset->get("tiles/show_name_on_square150x150")) { - name_on_tiles += " <uap:ShowOn Tile=\"square150x150Logo\" />\n"; - } - if ((bool)p_preset->get("tiles/show_name_on_wide310x150")) { - name_on_tiles += " <uap:ShowOn Tile=\"wide310x150Logo\" />\n"; - } - if ((bool)p_preset->get("tiles/show_name_on_square310x310")) { - name_on_tiles += " <uap:ShowOn Tile=\"square310x310Logo\" />\n"; - } - - String show_name_on_tiles = ""; - if (!name_on_tiles.is_empty()) { - show_name_on_tiles = "<uap:ShowNameOnTiles>\n" + name_on_tiles + " </uap:ShowNameOnTiles>"; - } - - result = result.replace("$name_on_tiles$", name_on_tiles); - - String rotations = ""; - if ((bool)p_preset->get("orientation/landscape")) { - rotations += " <uap:Rotation Preference=\"landscape\" />\n"; - } - if ((bool)p_preset->get("orientation/portrait")) { - rotations += " <uap:Rotation Preference=\"portrait\" />\n"; - } - if ((bool)p_preset->get("orientation/landscape_flipped")) { - rotations += " <uap:Rotation Preference=\"landscapeFlipped\" />\n"; - } - if ((bool)p_preset->get("orientation/portrait_flipped")) { - rotations += " <uap:Rotation Preference=\"portraitFlipped\" />\n"; - } - - String rotation_preference = ""; - if (!rotations.is_empty()) { - rotation_preference = "<uap:InitialRotationPreference>\n" + rotations + " </uap:InitialRotationPreference>"; - } - - result = result.replace("$rotation_preference$", rotation_preference); - - String capabilities_elements = ""; - const char **basic = uwp_capabilities; - while (*basic) { - if ((bool)p_preset->get("capabilities/" + String(*basic))) { - capabilities_elements += " <Capability Name=\"" + String(*basic) + "\" />\n"; - } - basic++; - } - const char **uap = uwp_uap_capabilities; - while (*uap) { - if ((bool)p_preset->get("capabilities/" + String(*uap))) { - capabilities_elements += " <uap:Capability Name=\"" + String(*uap) + "\" />\n"; - } - uap++; - } - const char **device = uwp_device_capabilities; - while (*device) { - if ((bool)p_preset->get("capabilities/" + String(*device))) { - capabilities_elements += " <DeviceCapability Name=\"" + String(*device) + "\" />\n"; - } - device++; - } - - if (!((bool)p_preset->get("capabilities/internetClient")) && p_give_internet) { - capabilities_elements += " <Capability Name=\"internetClient\" />\n"; - } - - String capabilities_string = "<Capabilities />"; - if (!capabilities_elements.is_empty()) { - capabilities_string = "<Capabilities>\n" + capabilities_elements + " </Capabilities>"; - } - - result = result.replace("$capabilities_place$", capabilities_string); - - Vector<uint8_t> r_ret; - r_ret.resize(result.length()); - - for (int i = 0; i < result.length(); i++) { - r_ret.write[i] = result.utf8().get(i); - } - - return r_ret; - } - - Vector<uint8_t> _get_image_data(const Ref<EditorExportPreset> &p_preset, const String &p_path) { - Vector<uint8_t> data; - StreamTexture2D *texture = nullptr; - - if (p_path.find("StoreLogo") != -1) { - texture = p_preset->get("images/store_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/store_logo"))); - } else if (p_path.find("Square44x44Logo") != -1) { - texture = p_preset->get("images/square44x44_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/square44x44_logo"))); - } else if (p_path.find("Square71x71Logo") != -1) { - texture = p_preset->get("images/square71x71_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/square71x71_logo"))); - } else if (p_path.find("Square150x150Logo") != -1) { - texture = p_preset->get("images/square150x150_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/square150x150_logo"))); - } else if (p_path.find("Square310x310Logo") != -1) { - texture = p_preset->get("images/square310x310_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/square310x310_logo"))); - } else if (p_path.find("Wide310x150Logo") != -1) { - texture = p_preset->get("images/wide310x150_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/wide310x150_logo"))); - } else if (p_path.find("SplashScreen") != -1) { - texture = p_preset->get("images/splash_screen").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/splash_screen"))); - } else { - ERR_PRINT("Unable to load logo"); - } - - if (!texture) { - return data; - } - - String tmp_path = EditorPaths::get_singleton()->get_cache_dir().plus_file("uwp_tmp_logo.png"); - - Error err = texture->get_image()->save_png(tmp_path); - - if (err != OK) { - String err_string = "Couldn't save temp logo file."; - - EditorNode::add_io_error(err_string); - ERR_FAIL_V_MSG(data, err_string); - } - - FileAccess *f = FileAccess::open(tmp_path, FileAccess::READ, &err); - - if (err != OK) { - String err_string = "Couldn't open temp logo file."; - // Cleanup generated file. - DirAccess::remove_file_or_error(tmp_path); - EditorNode::add_io_error(err_string); - ERR_FAIL_V_MSG(data, err_string); - } - - data.resize(f->get_length()); - f->get_buffer(data.ptrw(), data.size()); - - f->close(); - memdelete(f); - DirAccess::remove_file_or_error(tmp_path); - - return data; - } - - static bool _should_compress_asset(const String &p_path, const Vector<uint8_t> &p_data) { - /* TODO: This was copied verbatim from Android export. It should be - * refactored to the parent class and also be used for .zip export. - */ - - /* - * By not compressing files with little or not benefit in doing so, - * a performance gain is expected at runtime. Moreover, if the APK is - * zip-aligned, assets stored as they are can be efficiently read by - * Android by memory-mapping them. - */ - - // -- Unconditional uncompress to mimic AAPT plus some other - - static const char *unconditional_compress_ext[] = { - // From https://github.com/android/platform_frameworks_base/blob/master/tools/aapt/Package.cpp - // These formats are already compressed, or don't compress well: - ".jpg", ".jpeg", ".png", ".gif", - ".wav", ".mp2", ".mp3", ".ogg", ".aac", - ".mpg", ".mpeg", ".mid", ".midi", ".smf", ".jet", - ".rtttl", ".imy", ".xmf", ".mp4", ".m4a", - ".m4v", ".3gp", ".3gpp", ".3g2", ".3gpp2", - ".amr", ".awb", ".wma", ".wmv", - // Godot-specific: - ".webp", // Same reasoning as .png - ".cfb", // Don't let small config files slow-down startup - ".scn", // Binary scenes are usually already compressed - ".stex", // Streamable textures are usually already compressed - // Trailer for easier processing - nullptr - }; - - for (const char **ext = unconditional_compress_ext; *ext; ++ext) { - if (p_path.to_lower().ends_with(String(*ext))) { - return false; - } - } - - // -- Compressed resource? - - if (p_data.size() >= 4 && p_data[0] == 'R' && p_data[1] == 'S' && p_data[2] == 'C' && p_data[3] == 'C') { - // Already compressed - return false; - } - - // --- TODO: Decide on texture resources according to their image compression setting - - return true; - } - - static Error save_appx_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key) { - AppxPackager *packager = (AppxPackager *)p_userdata; - String dst_path = p_path.replace_first("res://", "game/"); - - return packager->add_file(dst_path, p_data.ptr(), p_data.size(), p_file, p_total, _should_compress_asset(p_path, p_data)); - } - -public: - virtual String get_name() const override { - return "UWP"; - } - virtual String get_os_name() const override { - return "UWP"; - } - - virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override { - List<String> list; - list.push_back("appx"); - return list; - } - - virtual Ref<Texture2D> get_logo() const override { - return logo; - } - - virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) override { - r_features->push_back("s3tc"); - r_features->push_back("etc"); - switch ((int)p_preset->get("architecture/target")) { - case EditorExportPlatformUWP::ARM: { - r_features->push_back("arm"); - } break; - case EditorExportPlatformUWP::X86: { - r_features->push_back("32"); - } break; - case EditorExportPlatformUWP::X64: { - r_features->push_back("64"); - } break; - } - } - - virtual void get_export_options(List<ExportOption> *r_options) override { - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), "")); - - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "architecture/target", PROPERTY_HINT_ENUM, "arm,x86,x64"), 1)); - - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "command_line/extra_args"), "")); - - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/display_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/short_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/unique_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game.Name"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/description"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/publisher", PROPERTY_HINT_PLACEHOLDER_TEXT, "CN=CompanyName"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/publisher_display_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Company Name"), "")); - - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "identity/product_guid", PROPERTY_HINT_PLACEHOLDER_TEXT, "00000000-0000-0000-0000-000000000000"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "identity/publisher_guid", PROPERTY_HINT_PLACEHOLDER_TEXT, "00000000-0000-0000-0000-000000000000"), "")); - - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "signing/certificate", PROPERTY_HINT_GLOBAL_FILE, "*.pfx"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "signing/password"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "signing/algorithm", PROPERTY_HINT_ENUM, "MD5,SHA1,SHA256"), 2)); - - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/major"), 1)); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/minor"), 0)); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/build"), 0)); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/revision"), 0)); - - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "orientation/landscape"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "orientation/portrait"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "orientation/landscape_flipped"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "orientation/portrait_flipped"), true)); - - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "images/background_color"), "transparent")); - r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/store_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture2D"), Variant())); - r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/square44x44_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture2D"), Variant())); - r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/square71x71_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture2D"), Variant())); - r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/square150x150_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture2D"), Variant())); - r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/square310x310_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture2D"), Variant())); - r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/wide310x150_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture2D"), Variant())); - r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/splash_screen", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture2D"), Variant())); - - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "tiles/show_name_on_square150x150"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "tiles/show_name_on_wide310x150"), false)); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "tiles/show_name_on_square310x310"), false)); - - // Capabilities - const char **basic = uwp_capabilities; - while (*basic) { - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/" + String(*basic)), false)); - basic++; - } - - const char **uap = uwp_uap_capabilities; - while (*uap) { - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/" + String(*uap)), false)); - uap++; - } - - const char **device = uwp_device_capabilities; - while (*device) { - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/" + String(*device)), false)); - device++; - } - } - - virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override { - String err; - bool valid = false; - - // Look for export templates (first official, and if defined custom templates). - - Platform arch = (Platform)(int)(p_preset->get("architecture/target")); - String platform_infix; - switch (arch) { - case EditorExportPlatformUWP::ARM: { - platform_infix = "arm"; - } break; - case EditorExportPlatformUWP::X86: { - platform_infix = "x86"; - } break; - case EditorExportPlatformUWP::X64: { - platform_infix = "x64"; - } break; - } - - bool dvalid = exists_export_template("uwp_" + platform_infix + "_debug.zip", &err); - bool rvalid = exists_export_template("uwp_" + platform_infix + "_release.zip", &err); - - if (p_preset->get("custom_template/debug") != "") { - dvalid = FileAccess::exists(p_preset->get("custom_template/debug")); - if (!dvalid) { - err += TTR("Custom debug template not found.") + "\n"; - } - } - if (p_preset->get("custom_template/release") != "") { - rvalid = FileAccess::exists(p_preset->get("custom_template/release")); - if (!rvalid) { - err += TTR("Custom release template not found.") + "\n"; - } - } - - valid = dvalid || rvalid; - r_missing_templates = !valid; - - // Validate the rest of the configuration. - - if (!_valid_resource_name(p_preset->get("package/short_name"))) { - valid = false; - err += TTR("Invalid package short name.") + "\n"; - } - - if (!_valid_resource_name(p_preset->get("package/unique_name"))) { - valid = false; - err += TTR("Invalid package unique name.") + "\n"; - } - - if (!_valid_resource_name(p_preset->get("package/publisher_display_name"))) { - valid = false; - err += TTR("Invalid package publisher display name.") + "\n"; - } - - if (!_valid_guid(p_preset->get("identity/product_guid"))) { - valid = false; - err += TTR("Invalid product GUID.") + "\n"; - } - - if (!_valid_guid(p_preset->get("identity/publisher_guid"))) { - valid = false; - err += TTR("Invalid publisher GUID.") + "\n"; - } - - if (!_valid_bgcolor(p_preset->get("images/background_color"))) { - valid = false; - err += TTR("Invalid background color.") + "\n"; - } - - if (!p_preset->get("images/store_logo").is_zero() && !_valid_image((Object::cast_to<StreamTexture2D>((Object *)p_preset->get("images/store_logo"))), 50, 50)) { - valid = false; - err += TTR("Invalid Store Logo image dimensions (should be 50x50).") + "\n"; - } - - if (!p_preset->get("images/square44x44_logo").is_zero() && !_valid_image((Object::cast_to<StreamTexture2D>((Object *)p_preset->get("images/square44x44_logo"))), 44, 44)) { - valid = false; - err += TTR("Invalid square 44x44 logo image dimensions (should be 44x44).") + "\n"; - } - - if (!p_preset->get("images/square71x71_logo").is_zero() && !_valid_image((Object::cast_to<StreamTexture2D>((Object *)p_preset->get("images/square71x71_logo"))), 71, 71)) { - valid = false; - err += TTR("Invalid square 71x71 logo image dimensions (should be 71x71).") + "\n"; - } - - if (!p_preset->get("images/square150x150_logo").is_zero() && !_valid_image((Object::cast_to<StreamTexture2D>((Object *)p_preset->get("images/square150x150_logo"))), 150, 150)) { - valid = false; - err += TTR("Invalid square 150x150 logo image dimensions (should be 150x150).") + "\n"; - } - - if (!p_preset->get("images/square310x310_logo").is_zero() && !_valid_image((Object::cast_to<StreamTexture2D>((Object *)p_preset->get("images/square310x310_logo"))), 310, 310)) { - valid = false; - err += TTR("Invalid square 310x310 logo image dimensions (should be 310x310).") + "\n"; - } - - if (!p_preset->get("images/wide310x150_logo").is_zero() && !_valid_image((Object::cast_to<StreamTexture2D>((Object *)p_preset->get("images/wide310x150_logo"))), 310, 150)) { - valid = false; - err += TTR("Invalid wide 310x150 logo image dimensions (should be 310x150).") + "\n"; - } - - if (!p_preset->get("images/splash_screen").is_zero() && !_valid_image((Object::cast_to<StreamTexture2D>((Object *)p_preset->get("images/splash_screen"))), 620, 300)) { - valid = false; - err += TTR("Invalid splash screen image dimensions (should be 620x300).") + "\n"; - } - - r_error = err; - return valid; - } - - virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override { - ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags); - - String src_appx; - - EditorProgress ep("export", "Exporting for UWP", 7, true); - - if (p_debug) { - src_appx = p_preset->get("custom_template/debug"); - } else { - src_appx = p_preset->get("custom_template/release"); - } - - src_appx = src_appx.strip_edges(); - - Platform arch = (Platform)(int)p_preset->get("architecture/target"); - - if (src_appx == "") { - String err, infix; - switch (arch) { - case ARM: { - infix = "_arm_"; - } break; - case X86: { - infix = "_x86_"; - } break; - case X64: { - infix = "_x64_"; - } break; - } - if (p_debug) { - src_appx = find_export_template("uwp" + infix + "debug.zip", &err); - } else { - src_appx = find_export_template("uwp" + infix + "release.zip", &err); - } - if (src_appx == "") { - EditorNode::add_io_error(err); - return ERR_FILE_NOT_FOUND; - } - } - - if (!DirAccess::exists(p_path.get_base_dir())) { - return ERR_FILE_BAD_PATH; - } - - Error err = OK; - - FileAccess *fa_pack = FileAccess::open(p_path, FileAccess::WRITE, &err); - ERR_FAIL_COND_V_MSG(err != OK, ERR_CANT_CREATE, "Cannot create file '" + p_path + "'."); - - AppxPackager packager; - packager.init(fa_pack); - - FileAccess *src_f = nullptr; - zlib_filefunc_def io = zipio_create_io_from_file(&src_f); - - if (ep.step("Creating package...", 0)) { - return ERR_SKIP; - } - - unzFile pkg = unzOpen2(src_appx.utf8().get_data(), &io); - - if (!pkg) { - EditorNode::add_io_error("Could not find template appx to export:\n" + src_appx); - return ERR_FILE_NOT_FOUND; - } - - int ret = unzGoToFirstFile(pkg); - - if (ep.step("Copying template files...", 1)) { - return ERR_SKIP; - } - - EditorNode::progress_add_task("template_files", "Template files", 100); - packager.set_progress_task("template_files"); - - int template_files_amount = 9; - int template_file_no = 1; - - while (ret == UNZ_OK) { - // get file name - unz_file_info info; - char fname[16834]; - ret = unzGetCurrentFileInfo(pkg, &info, fname, 16834, nullptr, 0, nullptr, 0); - - String path = fname; - - if (path.ends_with("/")) { - // Ignore directories - ret = unzGoToNextFile(pkg); - continue; - } - - Vector<uint8_t> data; - bool do_read = true; - - if (path.begins_with("Assets/")) { - path = path.replace(".scale-100", ""); - - data = _get_image_data(p_preset, path); - if (data.size() > 0) { - do_read = false; - } - } - - //read - if (do_read) { - data.resize(info.uncompressed_size); - unzOpenCurrentFile(pkg); - unzReadCurrentFile(pkg, data.ptrw(), data.size()); - unzCloseCurrentFile(pkg); - } - - if (path == "AppxManifest.xml") { - data = _fix_manifest(p_preset, data, p_flags & (DEBUG_FLAG_DUMB_CLIENT | DEBUG_FLAG_REMOTE_DEBUG)); - } - - print_line("ADDING: " + path); - - err = packager.add_file(path, data.ptr(), data.size(), template_file_no++, template_files_amount, _should_compress_asset(path, data)); - if (err != OK) { - return err; - } - - ret = unzGoToNextFile(pkg); - } - - EditorNode::progress_end_task("template_files"); - - if (ep.step("Creating command line...", 2)) { - return ERR_SKIP; - } - - Vector<String> cl = ((String)p_preset->get("command_line/extra_args")).strip_edges().split(" "); - for (int i = 0; i < cl.size(); i++) { - if (cl[i].strip_edges().length() == 0) { - cl.remove(i); - i--; - } - } - - if (!(p_flags & DEBUG_FLAG_DUMB_CLIENT)) { - cl.push_back("--path"); - cl.push_back("game"); - } - - gen_export_flags(cl, p_flags); - - // Command line file - Vector<uint8_t> clf; - - // Argc - clf.resize(4); - encode_uint32(cl.size(), clf.ptrw()); - - for (int i = 0; i < cl.size(); i++) { - CharString txt = cl[i].utf8(); - int base = clf.size(); - clf.resize(base + 4 + txt.length()); - encode_uint32(txt.length(), &clf.write[base]); - memcpy(&clf.write[base + 4], txt.ptr(), txt.length()); - print_line(itos(i) + " param: " + cl[i]); - } - - err = packager.add_file("__cl__.cl", clf.ptr(), clf.size(), -1, -1, false); - if (err != OK) { - return err; - } - - if (ep.step("Adding project files...", 3)) { - return ERR_SKIP; - } - - EditorNode::progress_add_task("project_files", "Project Files", 100); - packager.set_progress_task("project_files"); - - err = export_project_files(p_preset, save_appx_file, &packager); - - EditorNode::progress_end_task("project_files"); - - if (ep.step("Closing package...", 7)) { - return ERR_SKIP; - } - - unzClose(pkg); - - packager.finish(); - -#ifdef WINDOWS_ENABLED - // Sign with signtool - String signtool_path = EditorSettings::get_singleton()->get("export/uwp/signtool"); - if (signtool_path == String()) { - return OK; - } - - if (!FileAccess::exists(signtool_path)) { - ERR_PRINT("Could not find signtool executable at " + signtool_path + ", aborting."); - return ERR_FILE_NOT_FOUND; - } - - static String algs[] = { "MD5", "SHA1", "SHA256" }; - - String cert_path = EditorSettings::get_singleton()->get("export/uwp/debug_certificate"); - String cert_pass = EditorSettings::get_singleton()->get("export/uwp/debug_password"); - int cert_alg = EditorSettings::get_singleton()->get("export/uwp/debug_algorithm"); - - if (!p_debug) { - cert_path = p_preset->get("signing/certificate"); - cert_pass = p_preset->get("signing/password"); - cert_alg = p_preset->get("signing/algorithm"); - } - - if (cert_path == String()) { - return OK; // Certificate missing, don't try to sign - } - - if (!FileAccess::exists(cert_path)) { - ERR_PRINT("Could not find certificate file at " + cert_path + ", aborting."); - return ERR_FILE_NOT_FOUND; - } - - if (cert_alg < 0 || cert_alg > 2) { - ERR_PRINT("Invalid certificate algorithm " + itos(cert_alg) + ", aborting."); - return ERR_INVALID_DATA; - } - - List<String> args; - args.push_back("sign"); - args.push_back("/fd"); - args.push_back(algs[cert_alg]); - args.push_back("/a"); - args.push_back("/f"); - args.push_back(cert_path); - args.push_back("/p"); - args.push_back(cert_pass); - args.push_back(p_path); - - OS::get_singleton()->execute(signtool_path, args); -#endif // WINDOWS_ENABLED - - return OK; - } - - virtual void get_platform_features(List<String> *r_features) override { - r_features->push_back("pc"); - r_features->push_back("UWP"); - } - - virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) override { - } - - EditorExportPlatformUWP() { - Ref<Image> img = memnew(Image(_uwp_logo)); - logo.instantiate(); - logo->create_from_image(img); - } -}; +#include "export_plugin.h" void register_uwp_exporter() { #ifdef WINDOWS_ENABLED diff --git a/platform/uwp/export/export_plugin.cpp b/platform/uwp/export/export_plugin.cpp new file mode 100644 index 0000000000..5bd00b1549 --- /dev/null +++ b/platform/uwp/export/export_plugin.cpp @@ -0,0 +1,498 @@ +/*************************************************************************/ +/* export_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "export_plugin.h" + +#include "platform/uwp/logo.gen.h" + +String EditorExportPlatformUWP::get_name() const { + return "UWP"; +} +String EditorExportPlatformUWP::get_os_name() const { + return "UWP"; +} + +List<String> EditorExportPlatformUWP::get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const { + List<String> list; + list.push_back("appx"); + return list; +} + +Ref<Texture2D> EditorExportPlatformUWP::get_logo() const { + return logo; +} + +void EditorExportPlatformUWP::get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) { + r_features->push_back("s3tc"); + r_features->push_back("etc"); + switch ((int)p_preset->get("architecture/target")) { + case EditorExportPlatformUWP::ARM: { + r_features->push_back("arm"); + } break; + case EditorExportPlatformUWP::X86: { + r_features->push_back("32"); + } break; + case EditorExportPlatformUWP::X64: { + r_features->push_back("64"); + } break; + } +} + +void EditorExportPlatformUWP::get_export_options(List<ExportOption> *r_options) { + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/debug", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_template/release", PROPERTY_HINT_GLOBAL_FILE, "*.zip"), "")); + + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "architecture/target", PROPERTY_HINT_ENUM, "arm,x86,x64"), 1)); + + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "command_line/extra_args"), "")); + + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/display_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/short_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/unique_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game.Name"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/description"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/publisher", PROPERTY_HINT_PLACEHOLDER_TEXT, "CN=CompanyName"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/publisher_display_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Company Name"), "")); + + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "identity/product_guid", PROPERTY_HINT_PLACEHOLDER_TEXT, "00000000-0000-0000-0000-000000000000"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "identity/publisher_guid", PROPERTY_HINT_PLACEHOLDER_TEXT, "00000000-0000-0000-0000-000000000000"), "")); + + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "signing/certificate", PROPERTY_HINT_GLOBAL_FILE, "*.pfx"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "signing/password"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "signing/algorithm", PROPERTY_HINT_ENUM, "MD5,SHA1,SHA256"), 2)); + + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/major"), 1)); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/minor"), 0)); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/build"), 0)); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/revision"), 0)); + + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "orientation/landscape"), true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "orientation/portrait"), true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "orientation/landscape_flipped"), true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "orientation/portrait_flipped"), true)); + + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "images/background_color"), "transparent")); + r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/store_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture2D"), Variant())); + r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/square44x44_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture2D"), Variant())); + r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/square71x71_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture2D"), Variant())); + r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/square150x150_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture2D"), Variant())); + r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/square310x310_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture2D"), Variant())); + r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/wide310x150_logo", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture2D"), Variant())); + r_options->push_back(ExportOption(PropertyInfo(Variant::OBJECT, "images/splash_screen", PROPERTY_HINT_RESOURCE_TYPE, "StreamTexture2D"), Variant())); + + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "tiles/show_name_on_square150x150"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "tiles/show_name_on_wide310x150"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "tiles/show_name_on_square310x310"), false)); + + // Capabilities + const char **basic = uwp_capabilities; + while (*basic) { + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/" + String(*basic)), false)); + basic++; + } + + const char **uap = uwp_uap_capabilities; + while (*uap) { + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/" + String(*uap)), false)); + uap++; + } + + const char **device = uwp_device_capabilities; + while (*device) { + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/" + String(*device)), false)); + device++; + } +} + +bool EditorExportPlatformUWP::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const { + String err; + bool valid = false; + + // Look for export templates (first official, and if defined custom templates). + + Platform arch = (Platform)(int)(p_preset->get("architecture/target")); + String platform_infix; + switch (arch) { + case EditorExportPlatformUWP::ARM: { + platform_infix = "arm"; + } break; + case EditorExportPlatformUWP::X86: { + platform_infix = "x86"; + } break; + case EditorExportPlatformUWP::X64: { + platform_infix = "x64"; + } break; + } + + bool dvalid = exists_export_template("uwp_" + platform_infix + "_debug.zip", &err); + bool rvalid = exists_export_template("uwp_" + platform_infix + "_release.zip", &err); + + if (p_preset->get("custom_template/debug") != "") { + dvalid = FileAccess::exists(p_preset->get("custom_template/debug")); + if (!dvalid) { + err += TTR("Custom debug template not found.") + "\n"; + } + } + if (p_preset->get("custom_template/release") != "") { + rvalid = FileAccess::exists(p_preset->get("custom_template/release")); + if (!rvalid) { + err += TTR("Custom release template not found.") + "\n"; + } + } + + valid = dvalid || rvalid; + r_missing_templates = !valid; + + // Validate the rest of the configuration. + + if (!_valid_resource_name(p_preset->get("package/short_name"))) { + valid = false; + err += TTR("Invalid package short name.") + "\n"; + } + + if (!_valid_resource_name(p_preset->get("package/unique_name"))) { + valid = false; + err += TTR("Invalid package unique name.") + "\n"; + } + + if (!_valid_resource_name(p_preset->get("package/publisher_display_name"))) { + valid = false; + err += TTR("Invalid package publisher display name.") + "\n"; + } + + if (!_valid_guid(p_preset->get("identity/product_guid"))) { + valid = false; + err += TTR("Invalid product GUID.") + "\n"; + } + + if (!_valid_guid(p_preset->get("identity/publisher_guid"))) { + valid = false; + err += TTR("Invalid publisher GUID.") + "\n"; + } + + if (!_valid_bgcolor(p_preset->get("images/background_color"))) { + valid = false; + err += TTR("Invalid background color.") + "\n"; + } + + if (!p_preset->get("images/store_logo").is_zero() && !_valid_image((Object::cast_to<StreamTexture2D>((Object *)p_preset->get("images/store_logo"))), 50, 50)) { + valid = false; + err += TTR("Invalid Store Logo image dimensions (should be 50x50).") + "\n"; + } + + if (!p_preset->get("images/square44x44_logo").is_zero() && !_valid_image((Object::cast_to<StreamTexture2D>((Object *)p_preset->get("images/square44x44_logo"))), 44, 44)) { + valid = false; + err += TTR("Invalid square 44x44 logo image dimensions (should be 44x44).") + "\n"; + } + + if (!p_preset->get("images/square71x71_logo").is_zero() && !_valid_image((Object::cast_to<StreamTexture2D>((Object *)p_preset->get("images/square71x71_logo"))), 71, 71)) { + valid = false; + err += TTR("Invalid square 71x71 logo image dimensions (should be 71x71).") + "\n"; + } + + if (!p_preset->get("images/square150x150_logo").is_zero() && !_valid_image((Object::cast_to<StreamTexture2D>((Object *)p_preset->get("images/square150x150_logo"))), 150, 150)) { + valid = false; + err += TTR("Invalid square 150x150 logo image dimensions (should be 150x150).") + "\n"; + } + + if (!p_preset->get("images/square310x310_logo").is_zero() && !_valid_image((Object::cast_to<StreamTexture2D>((Object *)p_preset->get("images/square310x310_logo"))), 310, 310)) { + valid = false; + err += TTR("Invalid square 310x310 logo image dimensions (should be 310x310).") + "\n"; + } + + if (!p_preset->get("images/wide310x150_logo").is_zero() && !_valid_image((Object::cast_to<StreamTexture2D>((Object *)p_preset->get("images/wide310x150_logo"))), 310, 150)) { + valid = false; + err += TTR("Invalid wide 310x150 logo image dimensions (should be 310x150).") + "\n"; + } + + if (!p_preset->get("images/splash_screen").is_zero() && !_valid_image((Object::cast_to<StreamTexture2D>((Object *)p_preset->get("images/splash_screen"))), 620, 300)) { + valid = false; + err += TTR("Invalid splash screen image dimensions (should be 620x300).") + "\n"; + } + + r_error = err; + return valid; +} + +Error EditorExportPlatformUWP::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { + ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags); + + String src_appx; + + EditorProgress ep("export", "Exporting for UWP", 7, true); + + if (p_debug) { + src_appx = p_preset->get("custom_template/debug"); + } else { + src_appx = p_preset->get("custom_template/release"); + } + + src_appx = src_appx.strip_edges(); + + Platform arch = (Platform)(int)p_preset->get("architecture/target"); + + if (src_appx == "") { + String err, infix; + switch (arch) { + case ARM: { + infix = "_arm_"; + } break; + case X86: { + infix = "_x86_"; + } break; + case X64: { + infix = "_x64_"; + } break; + } + if (p_debug) { + src_appx = find_export_template("uwp" + infix + "debug.zip", &err); + } else { + src_appx = find_export_template("uwp" + infix + "release.zip", &err); + } + if (src_appx == "") { + EditorNode::add_io_error(err); + return ERR_FILE_NOT_FOUND; + } + } + + if (!DirAccess::exists(p_path.get_base_dir())) { + return ERR_FILE_BAD_PATH; + } + + Error err = OK; + + FileAccess *fa_pack = FileAccess::open(p_path, FileAccess::WRITE, &err); + ERR_FAIL_COND_V_MSG(err != OK, ERR_CANT_CREATE, "Cannot create file '" + p_path + "'."); + + AppxPackager packager; + packager.init(fa_pack); + + FileAccess *src_f = nullptr; + zlib_filefunc_def io = zipio_create_io_from_file(&src_f); + + if (ep.step("Creating package...", 0)) { + return ERR_SKIP; + } + + unzFile pkg = unzOpen2(src_appx.utf8().get_data(), &io); + + if (!pkg) { + EditorNode::add_io_error("Could not find template appx to export:\n" + src_appx); + return ERR_FILE_NOT_FOUND; + } + + int ret = unzGoToFirstFile(pkg); + + if (ep.step("Copying template files...", 1)) { + return ERR_SKIP; + } + + EditorNode::progress_add_task("template_files", "Template files", 100); + packager.set_progress_task("template_files"); + + int template_files_amount = 9; + int template_file_no = 1; + + while (ret == UNZ_OK) { + // get file name + unz_file_info info; + char fname[16834]; + ret = unzGetCurrentFileInfo(pkg, &info, fname, 16834, nullptr, 0, nullptr, 0); + + String path = fname; + + if (path.ends_with("/")) { + // Ignore directories + ret = unzGoToNextFile(pkg); + continue; + } + + Vector<uint8_t> data; + bool do_read = true; + + if (path.begins_with("Assets/")) { + path = path.replace(".scale-100", ""); + + data = _get_image_data(p_preset, path); + if (data.size() > 0) { + do_read = false; + } + } + + //read + if (do_read) { + data.resize(info.uncompressed_size); + unzOpenCurrentFile(pkg); + unzReadCurrentFile(pkg, data.ptrw(), data.size()); + unzCloseCurrentFile(pkg); + } + + if (path == "AppxManifest.xml") { + data = _fix_manifest(p_preset, data, p_flags & (DEBUG_FLAG_DUMB_CLIENT | DEBUG_FLAG_REMOTE_DEBUG)); + } + + print_line("ADDING: " + path); + + err = packager.add_file(path, data.ptr(), data.size(), template_file_no++, template_files_amount, _should_compress_asset(path, data)); + if (err != OK) { + return err; + } + + ret = unzGoToNextFile(pkg); + } + + EditorNode::progress_end_task("template_files"); + + if (ep.step("Creating command line...", 2)) { + return ERR_SKIP; + } + + Vector<String> cl = ((String)p_preset->get("command_line/extra_args")).strip_edges().split(" "); + for (int i = 0; i < cl.size(); i++) { + if (cl[i].strip_edges().length() == 0) { + cl.remove(i); + i--; + } + } + + if (!(p_flags & DEBUG_FLAG_DUMB_CLIENT)) { + cl.push_back("--path"); + cl.push_back("game"); + } + + gen_export_flags(cl, p_flags); + + // Command line file + Vector<uint8_t> clf; + + // Argc + clf.resize(4); + encode_uint32(cl.size(), clf.ptrw()); + + for (int i = 0; i < cl.size(); i++) { + CharString txt = cl[i].utf8(); + int base = clf.size(); + clf.resize(base + 4 + txt.length()); + encode_uint32(txt.length(), &clf.write[base]); + memcpy(&clf.write[base + 4], txt.ptr(), txt.length()); + print_line(itos(i) + " param: " + cl[i]); + } + + err = packager.add_file("__cl__.cl", clf.ptr(), clf.size(), -1, -1, false); + if (err != OK) { + return err; + } + + if (ep.step("Adding project files...", 3)) { + return ERR_SKIP; + } + + EditorNode::progress_add_task("project_files", "Project Files", 100); + packager.set_progress_task("project_files"); + + err = export_project_files(p_preset, save_appx_file, &packager); + + EditorNode::progress_end_task("project_files"); + + if (ep.step("Closing package...", 7)) { + return ERR_SKIP; + } + + unzClose(pkg); + + packager.finish(); + +#ifdef WINDOWS_ENABLED + // Sign with signtool + String signtool_path = EditorSettings::get_singleton()->get("export/uwp/signtool"); + if (signtool_path == String()) { + return OK; + } + + if (!FileAccess::exists(signtool_path)) { + ERR_PRINT("Could not find signtool executable at " + signtool_path + ", aborting."); + return ERR_FILE_NOT_FOUND; + } + + static String algs[] = { "MD5", "SHA1", "SHA256" }; + + String cert_path = EditorSettings::get_singleton()->get("export/uwp/debug_certificate"); + String cert_pass = EditorSettings::get_singleton()->get("export/uwp/debug_password"); + int cert_alg = EditorSettings::get_singleton()->get("export/uwp/debug_algorithm"); + + if (!p_debug) { + cert_path = p_preset->get("signing/certificate"); + cert_pass = p_preset->get("signing/password"); + cert_alg = p_preset->get("signing/algorithm"); + } + + if (cert_path == String()) { + return OK; // Certificate missing, don't try to sign + } + + if (!FileAccess::exists(cert_path)) { + ERR_PRINT("Could not find certificate file at " + cert_path + ", aborting."); + return ERR_FILE_NOT_FOUND; + } + + if (cert_alg < 0 || cert_alg > 2) { + ERR_PRINT("Invalid certificate algorithm " + itos(cert_alg) + ", aborting."); + return ERR_INVALID_DATA; + } + + List<String> args; + args.push_back("sign"); + args.push_back("/fd"); + args.push_back(algs[cert_alg]); + args.push_back("/a"); + args.push_back("/f"); + args.push_back(cert_path); + args.push_back("/p"); + args.push_back(cert_pass); + args.push_back(p_path); + + OS::get_singleton()->execute(signtool_path, args); +#endif // WINDOWS_ENABLED + + return OK; +} + +void EditorExportPlatformUWP::get_platform_features(List<String> *r_features) { + r_features->push_back("pc"); + r_features->push_back("UWP"); +} + +void EditorExportPlatformUWP::resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) { +} + +EditorExportPlatformUWP::EditorExportPlatformUWP() { + Ref<Image> img = memnew(Image(_uwp_logo)); + logo.instantiate(); + logo->create_from_image(img); +} diff --git a/platform/uwp/export/export_plugin.h b/platform/uwp/export/export_plugin.h new file mode 100644 index 0000000000..f295789254 --- /dev/null +++ b/platform/uwp/export/export_plugin.h @@ -0,0 +1,448 @@ +/*************************************************************************/ +/* export_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef UWP_EXPORT_PLUGIN_H +#define UWP_EXPORT_PLUGIN_H + +#include "core/config/project_settings.h" +#include "core/crypto/crypto_core.h" +#include "core/io/dir_access.h" +#include "core/io/file_access.h" +#include "core/io/marshalls.h" +#include "core/io/zip_io.h" +#include "core/object/class_db.h" +#include "core/version.h" +#include "editor/editor_export.h" +#include "editor/editor_node.h" + +#include "thirdparty/minizip/unzip.h" +#include "thirdparty/minizip/zip.h" + +#include "app_packager.h" + +#include <zlib.h> + +// Capabilities +static const char *uwp_capabilities[] = { + "allJoyn", + "codeGeneration", + "internetClient", + "internetClientServer", + "privateNetworkClientServer", + nullptr +}; +static const char *uwp_uap_capabilities[] = { + "appointments", + "blockedChatMessages", + "chat", + "contacts", + "enterpriseAuthentication", + "musicLibrary", + "objects3D", + "picturesLibrary", + "phoneCall", + "removableStorage", + "sharedUserCertificates", + "userAccountInformation", + "videosLibrary", + "voipCall", + nullptr +}; +static const char *uwp_device_capabilities[] = { + "bluetooth", + "location", + "microphone", + "proximity", + "webcam", + nullptr +}; + +class EditorExportPlatformUWP : public EditorExportPlatform { + GDCLASS(EditorExportPlatformUWP, EditorExportPlatform); + + Ref<ImageTexture> logo; + + enum Platform { + ARM, + X86, + X64 + }; + + bool _valid_resource_name(const String &p_name) const { + if (p_name.is_empty()) { + return false; + } + if (p_name.ends_with(".")) { + return false; + } + + static const char *invalid_names[] = { + "CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", + "COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9", + nullptr + }; + + const char **t = invalid_names; + while (*t) { + if (p_name == *t) { + return false; + } + t++; + } + + return true; + } + + bool _valid_guid(const String &p_guid) const { + Vector<String> parts = p_guid.split("-"); + + if (parts.size() != 5) { + return false; + } + if (parts[0].length() != 8) { + return false; + } + for (int i = 1; i < 4; i++) { + if (parts[i].length() != 4) { + return false; + } + } + if (parts[4].length() != 12) { + return false; + } + + return true; + } + + bool _valid_bgcolor(const String &p_color) const { + if (p_color.is_empty()) { + return true; + } + if (p_color.begins_with("#") && p_color.is_valid_html_color()) { + return true; + } + + // Colors from https://msdn.microsoft.com/en-us/library/windows/apps/dn934817.aspx + static const char *valid_colors[] = { + "aliceBlue", "antiqueWhite", "aqua", "aquamarine", "azure", "beige", + "bisque", "black", "blanchedAlmond", "blue", "blueViolet", "brown", + "burlyWood", "cadetBlue", "chartreuse", "chocolate", "coral", "cornflowerBlue", + "cornsilk", "crimson", "cyan", "darkBlue", "darkCyan", "darkGoldenrod", + "darkGray", "darkGreen", "darkKhaki", "darkMagenta", "darkOliveGreen", "darkOrange", + "darkOrchid", "darkRed", "darkSalmon", "darkSeaGreen", "darkSlateBlue", "darkSlateGray", + "darkTurquoise", "darkViolet", "deepPink", "deepSkyBlue", "dimGray", "dodgerBlue", + "firebrick", "floralWhite", "forestGreen", "fuchsia", "gainsboro", "ghostWhite", + "gold", "goldenrod", "gray", "green", "greenYellow", "honeydew", + "hotPink", "indianRed", "indigo", "ivory", "khaki", "lavender", + "lavenderBlush", "lawnGreen", "lemonChiffon", "lightBlue", "lightCoral", "lightCyan", + "lightGoldenrodYellow", "lightGreen", "lightGray", "lightPink", "lightSalmon", "lightSeaGreen", + "lightSkyBlue", "lightSlateGray", "lightSteelBlue", "lightYellow", "lime", "limeGreen", + "linen", "magenta", "maroon", "mediumAquamarine", "mediumBlue", "mediumOrchid", + "mediumPurple", "mediumSeaGreen", "mediumSlateBlue", "mediumSpringGreen", "mediumTurquoise", "mediumVioletRed", + "midnightBlue", "mintCream", "mistyRose", "moccasin", "navajoWhite", "navy", + "oldLace", "olive", "oliveDrab", "orange", "orangeRed", "orchid", + "paleGoldenrod", "paleGreen", "paleTurquoise", "paleVioletRed", "papayaWhip", "peachPuff", + "peru", "pink", "plum", "powderBlue", "purple", "red", + "rosyBrown", "royalBlue", "saddleBrown", "salmon", "sandyBrown", "seaGreen", + "seaShell", "sienna", "silver", "skyBlue", "slateBlue", "slateGray", + "snow", "springGreen", "steelBlue", "tan", "teal", "thistle", + "tomato", "transparent", "turquoise", "violet", "wheat", "white", + "whiteSmoke", "yellow", "yellowGreen", + nullptr + }; + + const char **color = valid_colors; + + while (*color) { + if (p_color == *color) { + return true; + } + color++; + } + + return false; + } + + bool _valid_image(const StreamTexture2D *p_image, int p_width, int p_height) const { + if (!p_image) { + return false; + } + + // TODO: Add resource creation or image rescaling to enable other scales: + // 1.25, 1.5, 2.0 + return p_width == p_image->get_width() && p_height == p_image->get_height(); + } + + Vector<uint8_t> _fix_manifest(const Ref<EditorExportPreset> &p_preset, const Vector<uint8_t> &p_template, bool p_give_internet) const { + String result = String::utf8((const char *)p_template.ptr(), p_template.size()); + + result = result.replace("$godot_version$", VERSION_FULL_NAME); + + result = result.replace("$identity_name$", p_preset->get("package/unique_name")); + result = result.replace("$publisher$", p_preset->get("package/publisher")); + + result = result.replace("$product_guid$", p_preset->get("identity/product_guid")); + result = result.replace("$publisher_guid$", p_preset->get("identity/publisher_guid")); + + String version = itos(p_preset->get("version/major")) + "." + itos(p_preset->get("version/minor")) + "." + itos(p_preset->get("version/build")) + "." + itos(p_preset->get("version/revision")); + result = result.replace("$version_string$", version); + + Platform arch = (Platform)(int)p_preset->get("architecture/target"); + String architecture = arch == ARM ? "arm" : (arch == X86 ? "x86" : "x64"); + result = result.replace("$architecture$", architecture); + + result = result.replace("$display_name$", String(p_preset->get("package/display_name")).is_empty() ? (String)ProjectSettings::get_singleton()->get("application/config/name") : String(p_preset->get("package/display_name"))); + + result = result.replace("$publisher_display_name$", p_preset->get("package/publisher_display_name")); + result = result.replace("$app_description$", p_preset->get("package/description")); + result = result.replace("$bg_color$", p_preset->get("images/background_color")); + result = result.replace("$short_name$", p_preset->get("package/short_name")); + + String name_on_tiles = ""; + if ((bool)p_preset->get("tiles/show_name_on_square150x150")) { + name_on_tiles += " <uap:ShowOn Tile=\"square150x150Logo\" />\n"; + } + if ((bool)p_preset->get("tiles/show_name_on_wide310x150")) { + name_on_tiles += " <uap:ShowOn Tile=\"wide310x150Logo\" />\n"; + } + if ((bool)p_preset->get("tiles/show_name_on_square310x310")) { + name_on_tiles += " <uap:ShowOn Tile=\"square310x310Logo\" />\n"; + } + + String show_name_on_tiles = ""; + if (!name_on_tiles.is_empty()) { + show_name_on_tiles = "<uap:ShowNameOnTiles>\n" + name_on_tiles + " </uap:ShowNameOnTiles>"; + } + + result = result.replace("$name_on_tiles$", name_on_tiles); + + String rotations = ""; + if ((bool)p_preset->get("orientation/landscape")) { + rotations += " <uap:Rotation Preference=\"landscape\" />\n"; + } + if ((bool)p_preset->get("orientation/portrait")) { + rotations += " <uap:Rotation Preference=\"portrait\" />\n"; + } + if ((bool)p_preset->get("orientation/landscape_flipped")) { + rotations += " <uap:Rotation Preference=\"landscapeFlipped\" />\n"; + } + if ((bool)p_preset->get("orientation/portrait_flipped")) { + rotations += " <uap:Rotation Preference=\"portraitFlipped\" />\n"; + } + + String rotation_preference = ""; + if (!rotations.is_empty()) { + rotation_preference = "<uap:InitialRotationPreference>\n" + rotations + " </uap:InitialRotationPreference>"; + } + + result = result.replace("$rotation_preference$", rotation_preference); + + String capabilities_elements = ""; + const char **basic = uwp_capabilities; + while (*basic) { + if ((bool)p_preset->get("capabilities/" + String(*basic))) { + capabilities_elements += " <Capability Name=\"" + String(*basic) + "\" />\n"; + } + basic++; + } + const char **uap = uwp_uap_capabilities; + while (*uap) { + if ((bool)p_preset->get("capabilities/" + String(*uap))) { + capabilities_elements += " <uap:Capability Name=\"" + String(*uap) + "\" />\n"; + } + uap++; + } + const char **device = uwp_device_capabilities; + while (*device) { + if ((bool)p_preset->get("capabilities/" + String(*device))) { + capabilities_elements += " <DeviceCapability Name=\"" + String(*device) + "\" />\n"; + } + device++; + } + + if (!((bool)p_preset->get("capabilities/internetClient")) && p_give_internet) { + capabilities_elements += " <Capability Name=\"internetClient\" />\n"; + } + + String capabilities_string = "<Capabilities />"; + if (!capabilities_elements.is_empty()) { + capabilities_string = "<Capabilities>\n" + capabilities_elements + " </Capabilities>"; + } + + result = result.replace("$capabilities_place$", capabilities_string); + + Vector<uint8_t> r_ret; + r_ret.resize(result.length()); + + for (int i = 0; i < result.length(); i++) { + r_ret.write[i] = result.utf8().get(i); + } + + return r_ret; + } + + Vector<uint8_t> _get_image_data(const Ref<EditorExportPreset> &p_preset, const String &p_path) { + Vector<uint8_t> data; + StreamTexture2D *texture = nullptr; + + if (p_path.find("StoreLogo") != -1) { + texture = p_preset->get("images/store_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/store_logo"))); + } else if (p_path.find("Square44x44Logo") != -1) { + texture = p_preset->get("images/square44x44_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/square44x44_logo"))); + } else if (p_path.find("Square71x71Logo") != -1) { + texture = p_preset->get("images/square71x71_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/square71x71_logo"))); + } else if (p_path.find("Square150x150Logo") != -1) { + texture = p_preset->get("images/square150x150_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/square150x150_logo"))); + } else if (p_path.find("Square310x310Logo") != -1) { + texture = p_preset->get("images/square310x310_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/square310x310_logo"))); + } else if (p_path.find("Wide310x150Logo") != -1) { + texture = p_preset->get("images/wide310x150_logo").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/wide310x150_logo"))); + } else if (p_path.find("SplashScreen") != -1) { + texture = p_preset->get("images/splash_screen").is_zero() ? nullptr : Object::cast_to<StreamTexture2D>(((Object *)p_preset->get("images/splash_screen"))); + } else { + ERR_PRINT("Unable to load logo"); + } + + if (!texture) { + return data; + } + + String tmp_path = EditorPaths::get_singleton()->get_cache_dir().plus_file("uwp_tmp_logo.png"); + + Error err = texture->get_image()->save_png(tmp_path); + + if (err != OK) { + String err_string = "Couldn't save temp logo file."; + + EditorNode::add_io_error(err_string); + ERR_FAIL_V_MSG(data, err_string); + } + + FileAccess *f = FileAccess::open(tmp_path, FileAccess::READ, &err); + + if (err != OK) { + String err_string = "Couldn't open temp logo file."; + // Cleanup generated file. + DirAccess::remove_file_or_error(tmp_path); + EditorNode::add_io_error(err_string); + ERR_FAIL_V_MSG(data, err_string); + } + + data.resize(f->get_length()); + f->get_buffer(data.ptrw(), data.size()); + + f->close(); + memdelete(f); + DirAccess::remove_file_or_error(tmp_path); + + return data; + } + + static bool _should_compress_asset(const String &p_path, const Vector<uint8_t> &p_data) { + /* TODO: This was copied verbatim from Android export. It should be + * refactored to the parent class and also be used for .zip export. + */ + + /* + * By not compressing files with little or not benefit in doing so, + * a performance gain is expected at runtime. Moreover, if the APK is + * zip-aligned, assets stored as they are can be efficiently read by + * Android by memory-mapping them. + */ + + // -- Unconditional uncompress to mimic AAPT plus some other + + static const char *unconditional_compress_ext[] = { + // From https://github.com/android/platform_frameworks_base/blob/master/tools/aapt/Package.cpp + // These formats are already compressed, or don't compress well: + ".jpg", ".jpeg", ".png", ".gif", + ".wav", ".mp2", ".mp3", ".ogg", ".aac", + ".mpg", ".mpeg", ".mid", ".midi", ".smf", ".jet", + ".rtttl", ".imy", ".xmf", ".mp4", ".m4a", + ".m4v", ".3gp", ".3gpp", ".3g2", ".3gpp2", + ".amr", ".awb", ".wma", ".wmv", + // Godot-specific: + ".webp", // Same reasoning as .png + ".cfb", // Don't let small config files slow-down startup + ".scn", // Binary scenes are usually already compressed + ".stex", // Streamable textures are usually already compressed + // Trailer for easier processing + nullptr + }; + + for (const char **ext = unconditional_compress_ext; *ext; ++ext) { + if (p_path.to_lower().ends_with(String(*ext))) { + return false; + } + } + + // -- Compressed resource? + + if (p_data.size() >= 4 && p_data[0] == 'R' && p_data[1] == 'S' && p_data[2] == 'C' && p_data[3] == 'C') { + // Already compressed + return false; + } + + // --- TODO: Decide on texture resources according to their image compression setting + + return true; + } + + static Error save_appx_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key) { + AppxPackager *packager = (AppxPackager *)p_userdata; + String dst_path = p_path.replace_first("res://", "game/"); + + return packager->add_file(dst_path, p_data.ptr(), p_data.size(), p_file, p_total, _should_compress_asset(p_path, p_data)); + } + +public: + virtual String get_name() const override; + virtual String get_os_name() const override; + + virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override; + + virtual Ref<Texture2D> get_logo() const override; + + virtual void get_preset_features(const Ref<EditorExportPreset> &p_preset, List<String> *r_features) override; + + virtual void get_export_options(List<ExportOption> *r_options) override; + + virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override; + + virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override; + + virtual void get_platform_features(List<String> *r_features) override; + + virtual void resolve_platform_feature_priorities(const Ref<EditorExportPreset> &p_preset, Set<String> &p_features) override; + + EditorExportPlatformUWP(); +}; + +#endif diff --git a/platform/windows/export/export.cpp b/platform/windows/export/export.cpp index 10f953f2ec..4ff42f3f62 100644 --- a/platform/windows/export/export.cpp +++ b/platform/windows/export/export.cpp @@ -28,316 +28,11 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "core/io/file_access.h" -#include "core/os/os.h" -#include "editor/editor_export.h" -#include "editor/editor_node.h" -#include "editor/editor_settings.h" -#include "platform/windows/logo.gen.h" +#include "export.h" -static Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size); - -class EditorExportPlatformWindows : public EditorExportPlatformPC { - void _rcedit_add_data(const Ref<EditorExportPreset> &p_preset, const String &p_path); - Error _code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path); - -public: - virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0); - virtual Error sign_shared_object(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path); - virtual void get_export_options(List<ExportOption> *r_options); -}; - -Error EditorExportPlatformWindows::sign_shared_object(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path) { - if (p_preset->get("codesign/enable")) { - return _code_sign(p_preset, p_path); - } else { - return OK; - } -} - -Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { - Error err = EditorExportPlatformPC::export_project(p_preset, p_debug, p_path, p_flags); - - if (err != OK) { - return err; - } - - _rcedit_add_data(p_preset, p_path); - - if (p_preset->get("codesign/enable") && err == OK) { - err = _code_sign(p_preset, p_path); - } - - return err; -} - -void EditorExportPlatformWindows::get_export_options(List<ExportOption> *r_options) { - EditorExportPlatformPC::get_export_options(r_options); - - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/enable"), false)); -#ifdef WINDOWS_ENABLED - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/identity_type", PROPERTY_HINT_ENUM, "Select automatically,Use PKCS12 file (specify *.PFX/*.P12 file),Use certificate store (specify SHA1 hash)"), 0)); -#endif - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/identity", PROPERTY_HINT_GLOBAL_FILE, "*.pfx,*.p12"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/password"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/timestamp"), true)); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/timestamp_server_url"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/digest_algorithm", PROPERTY_HINT_ENUM, "SHA1,SHA256"), 1)); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/description"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "codesign/custom_options"), PackedStringArray())); - - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_FILE, "*.ico"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/company_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Company Name"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_description"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/copyright"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/trademarks"), "")); -} - -void EditorExportPlatformWindows::_rcedit_add_data(const Ref<EditorExportPreset> &p_preset, const String &p_path) { - String rcedit_path = EditorSettings::get_singleton()->get("export/windows/rcedit"); - - if (rcedit_path == String()) { - return; - } - - if (!FileAccess::exists(rcedit_path)) { - ERR_PRINT("Could not find rcedit executable at " + rcedit_path + ", no icon or app information data will be included."); - return; - } - -#ifndef WINDOWS_ENABLED - // On non-Windows we need WINE to run rcedit - String wine_path = EditorSettings::get_singleton()->get("export/windows/wine"); - - if (wine_path != String() && !FileAccess::exists(wine_path)) { - ERR_PRINT("Could not find wine executable at " + wine_path + ", no icon or app information data will be included."); - return; - } - - if (wine_path == String()) { - wine_path = "wine"; // try to run wine from PATH - } -#endif - - String icon_path = ProjectSettings::get_singleton()->globalize_path(p_preset->get("application/icon")); - String file_verion = p_preset->get("application/file_version"); - String product_version = p_preset->get("application/product_version"); - String company_name = p_preset->get("application/company_name"); - String product_name = p_preset->get("application/product_name"); - String file_description = p_preset->get("application/file_description"); - String copyright = p_preset->get("application/copyright"); - String trademarks = p_preset->get("application/trademarks"); - String comments = p_preset->get("application/comments"); - - List<String> args; - args.push_back(p_path); - if (icon_path != String()) { - args.push_back("--set-icon"); - args.push_back(icon_path); - } - if (file_verion != String()) { - args.push_back("--set-file-version"); - args.push_back(file_verion); - } - if (product_version != String()) { - args.push_back("--set-product-version"); - args.push_back(product_version); - } - if (company_name != String()) { - args.push_back("--set-version-string"); - args.push_back("CompanyName"); - args.push_back(company_name); - } - if (product_name != String()) { - args.push_back("--set-version-string"); - args.push_back("ProductName"); - args.push_back(product_name); - } - if (file_description != String()) { - args.push_back("--set-version-string"); - args.push_back("FileDescription"); - args.push_back(file_description); - } - if (copyright != String()) { - args.push_back("--set-version-string"); - args.push_back("LegalCopyright"); - args.push_back(copyright); - } - if (trademarks != String()) { - args.push_back("--set-version-string"); - args.push_back("LegalTrademarks"); - args.push_back(trademarks); - } - -#ifdef WINDOWS_ENABLED - OS::get_singleton()->execute(rcedit_path, args); -#else - // On non-Windows we need WINE to run rcedit - args.push_front(rcedit_path); - OS::get_singleton()->execute(wine_path, args); -#endif -} - -Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path) { - List<String> args; - -#ifdef WINDOWS_ENABLED - String signtool_path = EditorSettings::get_singleton()->get("export/windows/signtool"); - if (signtool_path != String() && !FileAccess::exists(signtool_path)) { - ERR_PRINT("Could not find signtool executable at " + signtool_path + ", aborting."); - return ERR_FILE_NOT_FOUND; - } - if (signtool_path == String()) { - signtool_path = "signtool"; // try to run signtool from PATH - } -#else - String signtool_path = EditorSettings::get_singleton()->get("export/windows/osslsigncode"); - if (signtool_path != String() && !FileAccess::exists(signtool_path)) { - ERR_PRINT("Could not find osslsigncode executable at " + signtool_path + ", aborting."); - return ERR_FILE_NOT_FOUND; - } - if (signtool_path == String()) { - signtool_path = "osslsigncode"; // try to run signtool from PATH - } -#endif - - args.push_back("sign"); - - //identity -#ifdef WINDOWS_ENABLED - int id_type = p_preset->get("codesign/identity_type"); - if (id_type == 0) { //auto select - args.push_back("/a"); - } else if (id_type == 1) { //pkcs12 - if (p_preset->get("codesign/identity") != "") { - args.push_back("/f"); - args.push_back(p_preset->get("codesign/identity")); - } else { - EditorNode::add_io_error("codesign: no identity found"); - return FAILED; - } - } else if (id_type == 2) { //Windows certificate store - if (p_preset->get("codesign/identity") != "") { - args.push_back("/sha1"); - args.push_back(p_preset->get("codesign/identity")); - } else { - EditorNode::add_io_error("codesign: no identity found"); - return FAILED; - } - } else { - EditorNode::add_io_error("codesign: invalid identity type"); - return FAILED; - } -#else - if (p_preset->get("codesign/identity") != "") { - args.push_back("-pkcs12"); - args.push_back(p_preset->get("codesign/identity")); - } else { - EditorNode::add_io_error("codesign: no identity found"); - return FAILED; - } -#endif - - //password - if (p_preset->get("codesign/password") != "") { -#ifdef WINDOWS_ENABLED - args.push_back("/p"); -#else - args.push_back("-pass"); -#endif - args.push_back(p_preset->get("codesign/password")); - } - - //timestamp - if (p_preset->get("codesign/timestamp")) { - if (p_preset->get("codesign/timestamp_server") != "") { -#ifdef WINDOWS_ENABLED - args.push_back("/tr"); - args.push_back(p_preset->get("codesign/timestamp_server_url")); - args.push_back("/td"); - if ((int)p_preset->get("codesign/digest_algorithm") == 0) { - args.push_back("sha1"); - } else { - args.push_back("sha256"); - } -#else - args.push_back("-ts"); - args.push_back(p_preset->get("codesign/timestamp_server_url")); -#endif - } else { - EditorNode::add_io_error("codesign: invalid timestamp server"); - return FAILED; - } - } - - //digest -#ifdef WINDOWS_ENABLED - args.push_back("/fd"); -#else - args.push_back("-h"); -#endif - if ((int)p_preset->get("codesign/digest_algorithm") == 0) { - args.push_back("sha1"); - } else { - args.push_back("sha256"); - } - - //description - if (p_preset->get("codesign/description") != "") { -#ifdef WINDOWS_ENABLED - args.push_back("/d"); -#else - args.push_back("-n"); -#endif - args.push_back(p_preset->get("codesign/description")); - } - - //user options - PackedStringArray user_args = p_preset->get("codesign/custom_options"); - for (int i = 0; i < user_args.size(); i++) { - String user_arg = user_args[i].strip_edges(); - if (!user_arg.is_empty()) { - args.push_back(user_arg); - } - } - -#ifndef WINDOWS_ENABLED - args.push_back("-in"); -#endif - args.push_back(p_path); -#ifndef WINDOWS_ENABLED - args.push_back("-out"); - args.push_back(p_path + "_signed"); -#endif - - String str; - Error err = OS::get_singleton()->execute(signtool_path, args, &str, nullptr, true); - ERR_FAIL_COND_V(err != OK, err); - - print_line("codesign (" + p_path + "): " + str); -#ifndef WINDOWS_ENABLED - if (str.find("SignTool Error") != -1) { -#else - if (str.find("Failed") != -1) { -#endif - return FAILED; - } +#include "export_plugin.h" -#ifndef WINDOWS_ENABLED - DirAccessRef tmp_dir = DirAccess::create_for_path(p_path.get_base_dir()); - - err = tmp_dir->remove(p_path); - ERR_FAIL_COND_V(err != OK, err); - - err = tmp_dir->rename(p_path + "_signed", p_path); - ERR_FAIL_COND_V(err != OK, err); -#endif - - return OK; -} +static Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size); void register_windows_exporter() { EDITOR_DEF("export/windows/rcedit", ""); diff --git a/platform/windows/export/export.h b/platform/windows/export/export.h index 6a7131c73f..110e1439e2 100644 --- a/platform/windows/export/export.h +++ b/platform/windows/export/export.h @@ -28,4 +28,9 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ +#ifndef WINDOWS_EXPORT_H +#define WINDOWS_EXPORT_H + void register_windows_exporter(); + +#endif diff --git a/platform/windows/export/export_plugin.cpp b/platform/windows/export/export_plugin.cpp new file mode 100644 index 0000000000..165e86c066 --- /dev/null +++ b/platform/windows/export/export_plugin.cpp @@ -0,0 +1,323 @@ +/*************************************************************************/ +/* export_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "export_plugin.h" + +Error EditorExportPlatformWindows::sign_shared_object(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path) { + if (p_preset->get("codesign/enable")) { + return _code_sign(p_preset, p_path); + } else { + return OK; + } +} + +Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { + Error err = EditorExportPlatformPC::export_project(p_preset, p_debug, p_path, p_flags); + + if (err != OK) { + return err; + } + + _rcedit_add_data(p_preset, p_path); + + if (p_preset->get("codesign/enable") && err == OK) { + err = _code_sign(p_preset, p_path); + } + + return err; +} + +void EditorExportPlatformWindows::get_export_options(List<ExportOption> *r_options) { + EditorExportPlatformPC::get_export_options(r_options); + + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/enable"), false)); +#ifdef WINDOWS_ENABLED + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/identity_type", PROPERTY_HINT_ENUM, "Select automatically,Use PKCS12 file (specify *.PFX/*.P12 file),Use certificate store (specify SHA1 hash)"), 0)); +#endif + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/identity", PROPERTY_HINT_GLOBAL_FILE, "*.pfx,*.p12"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/password"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/timestamp"), true)); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/timestamp_server_url"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/digest_algorithm", PROPERTY_HINT_ENUM, "SHA1,SHA256"), 1)); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/description"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "codesign/custom_options"), PackedStringArray())); + + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_FILE, "*.ico"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/company_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Company Name"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_description"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/copyright"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/trademarks"), "")); +} + +void EditorExportPlatformWindows::_rcedit_add_data(const Ref<EditorExportPreset> &p_preset, const String &p_path) { + String rcedit_path = EditorSettings::get_singleton()->get("export/windows/rcedit"); + + if (rcedit_path == String()) { + return; + } + + if (!FileAccess::exists(rcedit_path)) { + ERR_PRINT("Could not find rcedit executable at " + rcedit_path + ", no icon or app information data will be included."); + return; + } + +#ifndef WINDOWS_ENABLED + // On non-Windows we need WINE to run rcedit + String wine_path = EditorSettings::get_singleton()->get("export/windows/wine"); + + if (wine_path != String() && !FileAccess::exists(wine_path)) { + ERR_PRINT("Could not find wine executable at " + wine_path + ", no icon or app information data will be included."); + return; + } + + if (wine_path == String()) { + wine_path = "wine"; // try to run wine from PATH + } +#endif + + String icon_path = ProjectSettings::get_singleton()->globalize_path(p_preset->get("application/icon")); + String file_verion = p_preset->get("application/file_version"); + String product_version = p_preset->get("application/product_version"); + String company_name = p_preset->get("application/company_name"); + String product_name = p_preset->get("application/product_name"); + String file_description = p_preset->get("application/file_description"); + String copyright = p_preset->get("application/copyright"); + String trademarks = p_preset->get("application/trademarks"); + String comments = p_preset->get("application/comments"); + + List<String> args; + args.push_back(p_path); + if (icon_path != String()) { + args.push_back("--set-icon"); + args.push_back(icon_path); + } + if (file_verion != String()) { + args.push_back("--set-file-version"); + args.push_back(file_verion); + } + if (product_version != String()) { + args.push_back("--set-product-version"); + args.push_back(product_version); + } + if (company_name != String()) { + args.push_back("--set-version-string"); + args.push_back("CompanyName"); + args.push_back(company_name); + } + if (product_name != String()) { + args.push_back("--set-version-string"); + args.push_back("ProductName"); + args.push_back(product_name); + } + if (file_description != String()) { + args.push_back("--set-version-string"); + args.push_back("FileDescription"); + args.push_back(file_description); + } + if (copyright != String()) { + args.push_back("--set-version-string"); + args.push_back("LegalCopyright"); + args.push_back(copyright); + } + if (trademarks != String()) { + args.push_back("--set-version-string"); + args.push_back("LegalTrademarks"); + args.push_back(trademarks); + } + +#ifdef WINDOWS_ENABLED + OS::get_singleton()->execute(rcedit_path, args); +#else + // On non-Windows we need WINE to run rcedit + args.push_front(rcedit_path); + OS::get_singleton()->execute(wine_path, args); +#endif +} + +Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path) { + List<String> args; + +#ifdef WINDOWS_ENABLED + String signtool_path = EditorSettings::get_singleton()->get("export/windows/signtool"); + if (signtool_path != String() && !FileAccess::exists(signtool_path)) { + ERR_PRINT("Could not find signtool executable at " + signtool_path + ", aborting."); + return ERR_FILE_NOT_FOUND; + } + if (signtool_path == String()) { + signtool_path = "signtool"; // try to run signtool from PATH + } +#else + String signtool_path = EditorSettings::get_singleton()->get("export/windows/osslsigncode"); + if (signtool_path != String() && !FileAccess::exists(signtool_path)) { + ERR_PRINT("Could not find osslsigncode executable at " + signtool_path + ", aborting."); + return ERR_FILE_NOT_FOUND; + } + if (signtool_path == String()) { + signtool_path = "osslsigncode"; // try to run signtool from PATH + } +#endif + + args.push_back("sign"); + + //identity +#ifdef WINDOWS_ENABLED + int id_type = p_preset->get("codesign/identity_type"); + if (id_type == 0) { //auto select + args.push_back("/a"); + } else if (id_type == 1) { //pkcs12 + if (p_preset->get("codesign/identity") != "") { + args.push_back("/f"); + args.push_back(p_preset->get("codesign/identity")); + } else { + EditorNode::add_io_error("codesign: no identity found"); + return FAILED; + } + } else if (id_type == 2) { //Windows certificate store + if (p_preset->get("codesign/identity") != "") { + args.push_back("/sha1"); + args.push_back(p_preset->get("codesign/identity")); + } else { + EditorNode::add_io_error("codesign: no identity found"); + return FAILED; + } + } else { + EditorNode::add_io_error("codesign: invalid identity type"); + return FAILED; + } +#else + if (p_preset->get("codesign/identity") != "") { + args.push_back("-pkcs12"); + args.push_back(p_preset->get("codesign/identity")); + } else { + EditorNode::add_io_error("codesign: no identity found"); + return FAILED; + } +#endif + + //password + if (p_preset->get("codesign/password") != "") { +#ifdef WINDOWS_ENABLED + args.push_back("/p"); +#else + args.push_back("-pass"); +#endif + args.push_back(p_preset->get("codesign/password")); + } + + //timestamp + if (p_preset->get("codesign/timestamp")) { + if (p_preset->get("codesign/timestamp_server") != "") { +#ifdef WINDOWS_ENABLED + args.push_back("/tr"); + args.push_back(p_preset->get("codesign/timestamp_server_url")); + args.push_back("/td"); + if ((int)p_preset->get("codesign/digest_algorithm") == 0) { + args.push_back("sha1"); + } else { + args.push_back("sha256"); + } +#else + args.push_back("-ts"); + args.push_back(p_preset->get("codesign/timestamp_server_url")); +#endif + } else { + EditorNode::add_io_error("codesign: invalid timestamp server"); + return FAILED; + } + } + + //digest +#ifdef WINDOWS_ENABLED + args.push_back("/fd"); +#else + args.push_back("-h"); +#endif + if ((int)p_preset->get("codesign/digest_algorithm") == 0) { + args.push_back("sha1"); + } else { + args.push_back("sha256"); + } + + //description + if (p_preset->get("codesign/description") != "") { +#ifdef WINDOWS_ENABLED + args.push_back("/d"); +#else + args.push_back("-n"); +#endif + args.push_back(p_preset->get("codesign/description")); + } + + //user options + PackedStringArray user_args = p_preset->get("codesign/custom_options"); + for (int i = 0; i < user_args.size(); i++) { + String user_arg = user_args[i].strip_edges(); + if (!user_arg.is_empty()) { + args.push_back(user_arg); + } + } + +#ifndef WINDOWS_ENABLED + args.push_back("-in"); +#endif + args.push_back(p_path); +#ifndef WINDOWS_ENABLED + args.push_back("-out"); + args.push_back(p_path + "_signed"); +#endif + + String str; + Error err = OS::get_singleton()->execute(signtool_path, args, &str, nullptr, true); + ERR_FAIL_COND_V(err != OK, err); + + print_line("codesign (" + p_path + "): " + str); +#ifndef WINDOWS_ENABLED + if (str.find("SignTool Error") != -1) { +#else + if (str.find("Failed") != -1) { +#endif + return FAILED; + } + +#ifndef WINDOWS_ENABLED + DirAccessRef tmp_dir = DirAccess::create_for_path(p_path.get_base_dir()); + + err = tmp_dir->remove(p_path); + ERR_FAIL_COND_V(err != OK, err); + + err = tmp_dir->rename(p_path + "_signed", p_path); + ERR_FAIL_COND_V(err != OK, err); +#endif + + return OK; +} diff --git a/platform/windows/export/export_plugin.h b/platform/windows/export/export_plugin.h new file mode 100644 index 0000000000..11d3826410 --- /dev/null +++ b/platform/windows/export/export_plugin.h @@ -0,0 +1,51 @@ +/*************************************************************************/ +/* export_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef WINDOWS_EXPORT_PLUGIN_H +#define WINDOWS_EXPORT_PLUGIN_H + +#include "core/io/file_access.h" +#include "core/os/os.h" +#include "editor/editor_export.h" +#include "editor/editor_node.h" +#include "editor/editor_settings.h" +#include "platform/windows/logo.gen.h" + +class EditorExportPlatformWindows : public EditorExportPlatformPC { + void _rcedit_add_data(const Ref<EditorExportPreset> &p_preset, const String &p_path); + Error _code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path); + +public: + virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0); + virtual Error sign_shared_object(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path); + virtual void get_export_options(List<ExportOption> *r_options); +}; + +#endif diff --git a/scene/2d/collision_object_2d.cpp b/scene/2d/collision_object_2d.cpp index 3ba3a4eec5..60f29ca163 100644 --- a/scene/2d/collision_object_2d.cpp +++ b/scene/2d/collision_object_2d.cpp @@ -147,36 +147,40 @@ uint32_t CollisionObject2D::get_collision_mask() const { return collision_mask; } -void CollisionObject2D::set_collision_layer_bit(int p_bit, bool p_value) { - ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision layer bit must be between 0 and 31 inclusive."); +void CollisionObject2D::set_collision_layer_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive."); uint32_t collision_layer = get_collision_layer(); if (p_value) { - collision_layer |= 1 << p_bit; + collision_layer |= 1 << (p_layer_number - 1); } else { - collision_layer &= ~(1 << p_bit); + collision_layer &= ~(1 << (p_layer_number - 1)); } set_collision_layer(collision_layer); } -bool CollisionObject2D::get_collision_layer_bit(int p_bit) const { - ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision layer bit must be between 0 and 31 inclusive."); - return get_collision_layer() & (1 << p_bit); +bool CollisionObject2D::get_collision_layer_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive."); + return get_collision_layer() & (1 << (p_layer_number - 1)); } -void CollisionObject2D::set_collision_mask_bit(int p_bit, bool p_value) { - ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision mask bit must be between 0 and 31 inclusive."); +void CollisionObject2D::set_collision_mask_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive."); uint32_t mask = get_collision_mask(); if (p_value) { - mask |= 1 << p_bit; + mask |= 1 << (p_layer_number - 1); } else { - mask &= ~(1 << p_bit); + mask &= ~(1 << (p_layer_number - 1)); } set_collision_mask(mask); } -bool CollisionObject2D::get_collision_mask_bit(int p_bit) const { - ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision mask bit must be between 0 and 31 inclusive."); - return get_collision_mask() & (1 << p_bit); +bool CollisionObject2D::get_collision_mask_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive."); + return get_collision_mask() & (1 << (p_layer_number - 1)); } void CollisionObject2D::set_disable_mode(DisableMode p_mode) { @@ -565,10 +569,10 @@ void CollisionObject2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_collision_layer"), &CollisionObject2D::get_collision_layer); ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &CollisionObject2D::set_collision_mask); ClassDB::bind_method(D_METHOD("get_collision_mask"), &CollisionObject2D::get_collision_mask); - ClassDB::bind_method(D_METHOD("set_collision_layer_bit", "bit", "value"), &CollisionObject2D::set_collision_layer_bit); - ClassDB::bind_method(D_METHOD("get_collision_layer_bit", "bit"), &CollisionObject2D::get_collision_layer_bit); - ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &CollisionObject2D::set_collision_mask_bit); - ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &CollisionObject2D::get_collision_mask_bit); + ClassDB::bind_method(D_METHOD("set_collision_layer_value", "layer_number", "value"), &CollisionObject2D::set_collision_layer_value); + ClassDB::bind_method(D_METHOD("get_collision_layer_value", "layer_number"), &CollisionObject2D::get_collision_layer_value); + ClassDB::bind_method(D_METHOD("set_collision_mask_value", "layer_number", "value"), &CollisionObject2D::set_collision_mask_value); + ClassDB::bind_method(D_METHOD("get_collision_mask_value", "layer_number"), &CollisionObject2D::get_collision_mask_value); ClassDB::bind_method(D_METHOD("set_disable_mode", "mode"), &CollisionObject2D::set_disable_mode); ClassDB::bind_method(D_METHOD("get_disable_mode"), &CollisionObject2D::get_disable_mode); ClassDB::bind_method(D_METHOD("set_pickable", "enabled"), &CollisionObject2D::set_pickable); diff --git a/scene/2d/collision_object_2d.h b/scene/2d/collision_object_2d.h index eca53eecfc..11e11d1382 100644 --- a/scene/2d/collision_object_2d.h +++ b/scene/2d/collision_object_2d.h @@ -107,11 +107,11 @@ public: void set_collision_mask(uint32_t p_mask); uint32_t get_collision_mask() const; - void set_collision_layer_bit(int p_bit, bool p_value); - bool get_collision_layer_bit(int p_bit) const; + void set_collision_layer_value(int p_layer_number, bool p_value); + bool get_collision_layer_value(int p_layer_number) const; - void set_collision_mask_bit(int p_bit, bool p_value); - bool get_collision_mask_bit(int p_bit) const; + void set_collision_mask_value(int p_layer_number, bool p_value); + bool get_collision_mask_value(int p_layer_number) const; void set_disable_mode(DisableMode p_mode); DisableMode get_disable_mode() const; diff --git a/scene/2d/line_builder.cpp b/scene/2d/line_builder.cpp index c478f03356..a8a2639ccf 100644 --- a/scene/2d/line_builder.cpp +++ b/scene/2d/line_builder.cpp @@ -62,14 +62,6 @@ static SegmentIntersectionResult segment_intersection( return SEGMENT_PARALLEL; } -// TODO I'm pretty sure there is an even faster way to swap things -template <typename T> -static inline void swap(T &a, T &b) { - T tmp = a; - a = b; - b = tmp; -} - static float calculate_total_distance(const Vector<Vector2> &points) { float d = 0.f; for (int i = 1; i < points.size(); ++i) { diff --git a/scene/2d/ray_cast_2d.cpp b/scene/2d/ray_cast_2d.cpp index 602a0f2115..546536f546 100644 --- a/scene/2d/ray_cast_2d.cpp +++ b/scene/2d/ray_cast_2d.cpp @@ -54,20 +54,22 @@ uint32_t RayCast2D::get_collision_mask() const { return collision_mask; } -void RayCast2D::set_collision_mask_bit(int p_bit, bool p_value) { - ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision mask bit must be between 0 and 31 inclusive."); +void RayCast2D::set_collision_mask_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive."); uint32_t mask = get_collision_mask(); if (p_value) { - mask |= 1 << p_bit; + mask |= 1 << (p_layer_number - 1); } else { - mask &= ~(1 << p_bit); + mask &= ~(1 << (p_layer_number - 1)); } set_collision_mask(mask); } -bool RayCast2D::get_collision_mask_bit(int p_bit) const { - ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision mask bit must be between 0 and 31 inclusive."); - return get_collision_mask() & (1 << p_bit); +bool RayCast2D::get_collision_mask_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive."); + return get_collision_mask() & (1 << (p_layer_number - 1)); } bool RayCast2D::is_colliding() const { @@ -323,8 +325,8 @@ void RayCast2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &RayCast2D::set_collision_mask); ClassDB::bind_method(D_METHOD("get_collision_mask"), &RayCast2D::get_collision_mask); - ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &RayCast2D::set_collision_mask_bit); - ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &RayCast2D::get_collision_mask_bit); + ClassDB::bind_method(D_METHOD("set_collision_mask_value", "layer_number", "value"), &RayCast2D::set_collision_mask_value); + ClassDB::bind_method(D_METHOD("get_collision_mask_value", "layer_number"), &RayCast2D::get_collision_mask_value); ClassDB::bind_method(D_METHOD("set_exclude_parent_body", "mask"), &RayCast2D::set_exclude_parent_body); ClassDB::bind_method(D_METHOD("get_exclude_parent_body"), &RayCast2D::get_exclude_parent_body); diff --git a/scene/2d/ray_cast_2d.h b/scene/2d/ray_cast_2d.h index 984c6bda49..65b6e7899b 100644 --- a/scene/2d/ray_cast_2d.h +++ b/scene/2d/ray_cast_2d.h @@ -74,8 +74,8 @@ public: void set_collision_mask(uint32_t p_mask); uint32_t get_collision_mask() const; - void set_collision_mask_bit(int p_bit, bool p_value); - bool get_collision_mask_bit(int p_bit) const; + void set_collision_mask_value(int p_layer_number, bool p_value); + bool get_collision_mask_value(int p_layer_number) const; void set_exclude_parent_body(bool p_exclude_parent_body); bool get_exclude_parent_body() const; diff --git a/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp index e504277a55..9dc1df202c 100644 --- a/scene/3d/camera_3d.cpp +++ b/scene/3d/camera_3d.cpp @@ -499,8 +499,8 @@ void Camera3D::_bind_methods() { ClassDB::bind_method(D_METHOD("is_position_in_frustum", "world_point"), &Camera3D::is_position_in_frustum); ClassDB::bind_method(D_METHOD("get_camera_rid"), &Camera3D::get_camera); - ClassDB::bind_method(D_METHOD("set_cull_mask_bit", "layer", "enable"), &Camera3D::set_cull_mask_bit); - ClassDB::bind_method(D_METHOD("get_cull_mask_bit", "layer"), &Camera3D::get_cull_mask_bit); + ClassDB::bind_method(D_METHOD("set_cull_mask_value", "layer_number", "value"), &Camera3D::set_cull_mask_value); + ClassDB::bind_method(D_METHOD("get_cull_mask_value", "layer_number"), &Camera3D::get_cull_mask_value); //ClassDB::bind_method(D_METHOD("_camera_make_current"),&Camera::_camera_make_current ); @@ -592,18 +592,22 @@ uint32_t Camera3D::get_cull_mask() const { return layers; } -void Camera3D::set_cull_mask_bit(int p_layer, bool p_enable) { - ERR_FAIL_INDEX(p_layer, 32); - if (p_enable) { - set_cull_mask(layers | (1 << p_layer)); +void Camera3D::set_cull_mask_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Render layer number must be between 1 and 20 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 20, "Render layer number must be between 1 and 20 inclusive."); + uint32_t mask = get_cull_mask(); + if (p_value) { + mask |= 1 << (p_layer_number - 1); } else { - set_cull_mask(layers & (~(1 << p_layer))); + mask &= ~(1 << (p_layer_number - 1)); } + set_cull_mask(mask); } -bool Camera3D::get_cull_mask_bit(int p_layer) const { - ERR_FAIL_INDEX_V(p_layer, 32, false); - return (layers & (1 << p_layer)); +bool Camera3D::get_cull_mask_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Render layer number must be between 1 and 20 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 20, false, "Render layer number must be between 1 and 20 inclusive."); + return layers & (1 << (p_layer_number - 1)); } Vector<Plane> Camera3D::get_frustum() const { @@ -767,20 +771,22 @@ uint32_t ClippedCamera3D::get_collision_mask() const { return collision_mask; } -void ClippedCamera3D::set_collision_mask_bit(int p_bit, bool p_value) { - ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision layer bit must be between 0 and 31 inclusive."); +void ClippedCamera3D::set_collision_mask_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive."); uint32_t mask = get_collision_mask(); if (p_value) { - mask |= 1 << p_bit; + mask |= 1 << (p_layer_number - 1); } else { - mask &= ~(1 << p_bit); + mask &= ~(1 << (p_layer_number - 1)); } set_collision_mask(mask); } -bool ClippedCamera3D::get_collision_mask_bit(int p_bit) const { - ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision mask bit must be between 0 and 31 inclusive."); - return get_collision_mask() & (1 << p_bit); +bool ClippedCamera3D::get_collision_mask_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive."); + return get_collision_mask() & (1 << (p_layer_number - 1)); } void ClippedCamera3D::add_exception_rid(const RID &p_rid) { @@ -843,8 +849,8 @@ void ClippedCamera3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &ClippedCamera3D::set_collision_mask); ClassDB::bind_method(D_METHOD("get_collision_mask"), &ClippedCamera3D::get_collision_mask); - ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &ClippedCamera3D::set_collision_mask_bit); - ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &ClippedCamera3D::get_collision_mask_bit); + ClassDB::bind_method(D_METHOD("set_collision_mask_value", "layer_number", "value"), &ClippedCamera3D::set_collision_mask_value); + ClassDB::bind_method(D_METHOD("get_collision_mask_value", "layer_number"), &ClippedCamera3D::get_collision_mask_value); ClassDB::bind_method(D_METHOD("add_exception_rid", "rid"), &ClippedCamera3D::add_exception_rid); ClassDB::bind_method(D_METHOD("add_exception", "node"), &ClippedCamera3D::add_exception); diff --git a/scene/3d/camera_3d.h b/scene/3d/camera_3d.h index 61e3f51d0b..b7bf5566e7 100644 --- a/scene/3d/camera_3d.h +++ b/scene/3d/camera_3d.h @@ -147,8 +147,8 @@ public: void set_cull_mask(uint32_t p_layers); uint32_t get_cull_mask() const; - void set_cull_mask_bit(int p_layer, bool p_enable); - bool get_cull_mask_bit(int p_layer) const; + void set_cull_mask_value(int p_layer_number, bool p_enable); + bool get_cull_mask_value(int p_layer_number) const; virtual Vector<Plane> get_frustum() const; bool is_position_in_frustum(const Vector3 &p_position) const; @@ -224,8 +224,8 @@ public: void set_collision_mask(uint32_t p_mask); uint32_t get_collision_mask() const; - void set_collision_mask_bit(int p_bit, bool p_value); - bool get_collision_mask_bit(int p_bit) const; + void set_collision_mask_value(int p_layer_number, bool p_value); + bool get_collision_mask_value(int p_layer_number) const; void add_exception_rid(const RID &p_rid); void add_exception(const Object *p_object); diff --git a/scene/3d/collision_object_3d.cpp b/scene/3d/collision_object_3d.cpp index dd1f25da68..75bb2995d3 100644 --- a/scene/3d/collision_object_3d.cpp +++ b/scene/3d/collision_object_3d.cpp @@ -146,36 +146,40 @@ uint32_t CollisionObject3D::get_collision_mask() const { return collision_mask; } -void CollisionObject3D::set_collision_layer_bit(int p_bit, bool p_value) { - ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision layer bit must be between 0 and 31 inclusive."); +void CollisionObject3D::set_collision_layer_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive."); uint32_t collision_layer = get_collision_layer(); if (p_value) { - collision_layer |= 1 << p_bit; + collision_layer |= 1 << (p_layer_number - 1); } else { - collision_layer &= ~(1 << p_bit); + collision_layer &= ~(1 << (p_layer_number - 1)); } set_collision_layer(collision_layer); } -bool CollisionObject3D::get_collision_layer_bit(int p_bit) const { - ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision layer bit must be between 0 and 31 inclusive."); - return get_collision_layer() & (1 << p_bit); +bool CollisionObject3D::get_collision_layer_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive."); + return get_collision_layer() & (1 << (p_layer_number - 1)); } -void CollisionObject3D::set_collision_mask_bit(int p_bit, bool p_value) { - ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision mask bit must be between 0 and 31 inclusive."); +void CollisionObject3D::set_collision_mask_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive."); uint32_t mask = get_collision_mask(); if (p_value) { - mask |= 1 << p_bit; + mask |= 1 << (p_layer_number - 1); } else { - mask &= ~(1 << p_bit); + mask &= ~(1 << (p_layer_number - 1)); } set_collision_mask(mask); } -bool CollisionObject3D::get_collision_mask_bit(int p_bit) const { - ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision mask bit must be between 0 and 31 inclusive."); - return get_collision_mask() & (1 << p_bit); +bool CollisionObject3D::get_collision_mask_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive."); + return get_collision_mask() & (1 << (p_layer_number - 1)); } void CollisionObject3D::set_disable_mode(DisableMode p_mode) { @@ -423,10 +427,10 @@ void CollisionObject3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_collision_layer"), &CollisionObject3D::get_collision_layer); ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &CollisionObject3D::set_collision_mask); ClassDB::bind_method(D_METHOD("get_collision_mask"), &CollisionObject3D::get_collision_mask); - ClassDB::bind_method(D_METHOD("set_collision_layer_bit", "bit", "value"), &CollisionObject3D::set_collision_layer_bit); - ClassDB::bind_method(D_METHOD("get_collision_layer_bit", "bit"), &CollisionObject3D::get_collision_layer_bit); - ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &CollisionObject3D::set_collision_mask_bit); - ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &CollisionObject3D::get_collision_mask_bit); + ClassDB::bind_method(D_METHOD("set_collision_layer_value", "layer_number", "value"), &CollisionObject3D::set_collision_layer_value); + ClassDB::bind_method(D_METHOD("get_collision_layer_value", "layer_number"), &CollisionObject3D::get_collision_layer_value); + ClassDB::bind_method(D_METHOD("set_collision_mask_value", "layer_number", "value"), &CollisionObject3D::set_collision_mask_value); + ClassDB::bind_method(D_METHOD("get_collision_mask_value", "layer_number"), &CollisionObject3D::get_collision_mask_value); ClassDB::bind_method(D_METHOD("set_disable_mode", "mode"), &CollisionObject3D::set_disable_mode); ClassDB::bind_method(D_METHOD("get_disable_mode"), &CollisionObject3D::get_disable_mode); ClassDB::bind_method(D_METHOD("set_ray_pickable", "ray_pickable"), &CollisionObject3D::set_ray_pickable); diff --git a/scene/3d/collision_object_3d.h b/scene/3d/collision_object_3d.h index 7c30a5cd98..2e74d84465 100644 --- a/scene/3d/collision_object_3d.h +++ b/scene/3d/collision_object_3d.h @@ -119,11 +119,11 @@ public: void set_collision_mask(uint32_t p_mask); uint32_t get_collision_mask() const; - void set_collision_layer_bit(int p_bit, bool p_value); - bool get_collision_layer_bit(int p_bit) const; + void set_collision_layer_value(int p_layer_number, bool p_value); + bool get_collision_layer_value(int p_layer_number) const; - void set_collision_mask_bit(int p_bit, bool p_value); - bool get_collision_mask_bit(int p_bit) const; + void set_collision_mask_value(int p_layer_number, bool p_value); + bool get_collision_mask_value(int p_layer_number) const; void set_disable_mode(DisableMode p_mode); DisableMode get_disable_mode() const; diff --git a/scene/3d/occluder_instance_3d.cpp b/scene/3d/occluder_instance_3d.cpp index 3d1a27911b..f3e174c01b 100644 --- a/scene/3d/occluder_instance_3d.cpp +++ b/scene/3d/occluder_instance_3d.cpp @@ -195,18 +195,22 @@ uint32_t OccluderInstance3D::get_bake_mask() const { return bake_mask; } -void OccluderInstance3D::set_bake_mask_bit(int p_layer, bool p_enable) { - ERR_FAIL_INDEX(p_layer, 32); - if (p_enable) { - set_bake_mask(bake_mask | (1 << p_layer)); +void OccluderInstance3D::set_bake_mask_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Render layer number must be between 1 and 20 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 20, "Render layer number must be between 1 and 20 inclusive."); + uint32_t mask = get_bake_mask(); + if (p_value) { + mask |= 1 << (p_layer_number - 1); } else { - set_bake_mask(bake_mask & (~(1 << p_layer))); + mask &= ~(1 << (p_layer_number - 1)); } + set_bake_mask(mask); } -bool OccluderInstance3D::get_bake_mask_bit(int p_layer) const { - ERR_FAIL_INDEX_V(p_layer, 32, false); - return (bake_mask & (1 << p_layer)); +bool OccluderInstance3D::get_bake_mask_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Render layer number must be between 1 and 20 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 20, false, "Render layer number must be between 1 and 20 inclusive."); + return bake_mask & (1 << (p_layer_number - 1)); } bool OccluderInstance3D::_bake_material_check(Ref<Material> p_material) { @@ -345,8 +349,8 @@ TypedArray<String> OccluderInstance3D::get_configuration_warnings() const { void OccluderInstance3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_bake_mask", "mask"), &OccluderInstance3D::set_bake_mask); ClassDB::bind_method(D_METHOD("get_bake_mask"), &OccluderInstance3D::get_bake_mask); - ClassDB::bind_method(D_METHOD("set_bake_mask_bit", "layer", "enabled"), &OccluderInstance3D::set_bake_mask_bit); - ClassDB::bind_method(D_METHOD("get_bake_mask_bit", "layer"), &OccluderInstance3D::get_bake_mask_bit); + ClassDB::bind_method(D_METHOD("set_bake_mask_value", "layer_number", "value"), &OccluderInstance3D::set_bake_mask_value); + ClassDB::bind_method(D_METHOD("get_bake_mask_value", "layer_number"), &OccluderInstance3D::get_bake_mask_value); ClassDB::bind_method(D_METHOD("set_occluder", "occluder"), &OccluderInstance3D::set_occluder); ClassDB::bind_method(D_METHOD("get_occluder"), &OccluderInstance3D::get_occluder); diff --git a/scene/3d/occluder_instance_3d.h b/scene/3d/occluder_instance_3d.h index d382cd090e..173614b80c 100644 --- a/scene/3d/occluder_instance_3d.h +++ b/scene/3d/occluder_instance_3d.h @@ -99,8 +99,9 @@ public: void set_bake_mask(uint32_t p_mask); uint32_t get_bake_mask() const; - void set_bake_mask_bit(int p_layer, bool p_enable); - bool get_bake_mask_bit(int p_layer) const; + void set_bake_mask_value(int p_layer_number, bool p_enable); + bool get_bake_mask_value(int p_layer_number) const; + BakeError bake(Node *p_from_node, String p_occluder_path = ""); OccluderInstance3D(); diff --git a/scene/3d/ray_cast_3d.cpp b/scene/3d/ray_cast_3d.cpp index 7356ce478b..e757d6d3f4 100644 --- a/scene/3d/ray_cast_3d.cpp +++ b/scene/3d/ray_cast_3d.cpp @@ -60,20 +60,22 @@ uint32_t RayCast3D::get_collision_mask() const { return collision_mask; } -void RayCast3D::set_collision_mask_bit(int p_bit, bool p_value) { - ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision mask bit must be between 0 and 31 inclusive."); +void RayCast3D::set_collision_mask_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive."); uint32_t mask = get_collision_mask(); if (p_value) { - mask |= 1 << p_bit; + mask |= 1 << (p_layer_number - 1); } else { - mask &= ~(1 << p_bit); + mask &= ~(1 << (p_layer_number - 1)); } set_collision_mask(mask); } -bool RayCast3D::get_collision_mask_bit(int p_bit) const { - ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision mask bit must be between 0 and 31 inclusive."); - return get_collision_mask() & (1 << p_bit); +bool RayCast3D::get_collision_mask_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive."); + return get_collision_mask() & (1 << (p_layer_number - 1)); } bool RayCast3D::is_colliding() const { @@ -303,8 +305,8 @@ void RayCast3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &RayCast3D::set_collision_mask); ClassDB::bind_method(D_METHOD("get_collision_mask"), &RayCast3D::get_collision_mask); - ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &RayCast3D::set_collision_mask_bit); - ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &RayCast3D::get_collision_mask_bit); + ClassDB::bind_method(D_METHOD("set_collision_mask_value", "layer_number", "value"), &RayCast3D::set_collision_mask_value); + ClassDB::bind_method(D_METHOD("get_collision_mask_value", "layer_number"), &RayCast3D::get_collision_mask_value); ClassDB::bind_method(D_METHOD("set_exclude_parent_body", "mask"), &RayCast3D::set_exclude_parent_body); ClassDB::bind_method(D_METHOD("get_exclude_parent_body"), &RayCast3D::get_exclude_parent_body); diff --git a/scene/3d/ray_cast_3d.h b/scene/3d/ray_cast_3d.h index 968cede9f2..3828bfb4c4 100644 --- a/scene/3d/ray_cast_3d.h +++ b/scene/3d/ray_cast_3d.h @@ -86,8 +86,8 @@ public: void set_collision_mask(uint32_t p_mask); uint32_t get_collision_mask() const; - void set_collision_mask_bit(int p_bit, bool p_value); - bool get_collision_mask_bit(int p_bit) const; + void set_collision_mask_value(int p_layer_number, bool p_value); + bool get_collision_mask_value(int p_layer_number) const; void set_exclude_parent_body(bool p_exclude_parent_body); bool get_exclude_parent_body() const; diff --git a/scene/3d/soft_body_3d.cpp b/scene/3d/soft_body_3d.cpp index a7ff0842d2..3dbeac5e97 100644 --- a/scene/3d/soft_body_3d.cpp +++ b/scene/3d/soft_body_3d.cpp @@ -327,11 +327,11 @@ void SoftBody3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_collision_layer", "collision_layer"), &SoftBody3D::set_collision_layer); ClassDB::bind_method(D_METHOD("get_collision_layer"), &SoftBody3D::get_collision_layer); - ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &SoftBody3D::set_collision_mask_bit); - ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &SoftBody3D::get_collision_mask_bit); + ClassDB::bind_method(D_METHOD("set_collision_mask_value", "layer_number", "value"), &SoftBody3D::set_collision_mask_value); + ClassDB::bind_method(D_METHOD("get_collision_mask_value", "layer_number"), &SoftBody3D::get_collision_mask_value); - ClassDB::bind_method(D_METHOD("set_collision_layer_bit", "bit", "value"), &SoftBody3D::set_collision_layer_bit); - ClassDB::bind_method(D_METHOD("get_collision_layer_bit", "bit"), &SoftBody3D::get_collision_layer_bit); + ClassDB::bind_method(D_METHOD("set_collision_layer_value", "layer_number", "value"), &SoftBody3D::set_collision_layer_value); + ClassDB::bind_method(D_METHOD("get_collision_layer_value", "layer_number"), &SoftBody3D::get_collision_layer_value); ClassDB::bind_method(D_METHOD("set_parent_collision_ignore", "parent_collision_ignore"), &SoftBody3D::set_parent_collision_ignore); ClassDB::bind_method(D_METHOD("get_parent_collision_ignore"), &SoftBody3D::get_parent_collision_ignore); @@ -515,36 +515,40 @@ uint32_t SoftBody3D::get_collision_layer() const { return collision_layer; } -void SoftBody3D::set_collision_mask_bit(int p_bit, bool p_value) { - ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision mask bit must be between 0 and 31 inclusive."); - uint32_t mask = get_collision_mask(); +void SoftBody3D::set_collision_layer_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive."); + uint32_t collision_layer = get_collision_layer(); if (p_value) { - mask |= 1 << p_bit; + collision_layer |= 1 << (p_layer_number - 1); } else { - mask &= ~(1 << p_bit); + collision_layer &= ~(1 << (p_layer_number - 1)); } - set_collision_mask(mask); + set_collision_layer(collision_layer); } -bool SoftBody3D::get_collision_mask_bit(int p_bit) const { - ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision mask bit must be between 0 and 31 inclusive."); - return get_collision_mask() & (1 << p_bit); +bool SoftBody3D::get_collision_layer_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive."); + return get_collision_layer() & (1 << (p_layer_number - 1)); } -void SoftBody3D::set_collision_layer_bit(int p_bit, bool p_value) { - ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision layer bit must be between 0 and 31 inclusive."); - uint32_t layer = get_collision_layer(); +void SoftBody3D::set_collision_mask_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive."); + uint32_t mask = get_collision_mask(); if (p_value) { - layer |= 1 << p_bit; + mask |= 1 << (p_layer_number - 1); } else { - layer &= ~(1 << p_bit); + mask &= ~(1 << (p_layer_number - 1)); } - set_collision_layer(layer); + set_collision_mask(mask); } -bool SoftBody3D::get_collision_layer_bit(int p_bit) const { - ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision layer bit must be between 0 and 31 inclusive."); - return get_collision_layer() & (1 << p_bit); +bool SoftBody3D::get_collision_mask_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive."); + return get_collision_mask() & (1 << (p_layer_number - 1)); } void SoftBody3D::set_disable_mode(DisableMode p_mode) { diff --git a/scene/3d/soft_body_3d.h b/scene/3d/soft_body_3d.h index 81aa0c10c6..46b185a32c 100644 --- a/scene/3d/soft_body_3d.h +++ b/scene/3d/soft_body_3d.h @@ -138,11 +138,11 @@ public: void set_collision_layer(uint32_t p_layer); uint32_t get_collision_layer() const; - void set_collision_mask_bit(int p_bit, bool p_value); - bool get_collision_mask_bit(int p_bit) const; + void set_collision_layer_value(int p_layer_number, bool p_value); + bool get_collision_layer_value(int p_layer_number) const; - void set_collision_layer_bit(int p_bit, bool p_value); - bool get_collision_layer_bit(int p_bit) const; + void set_collision_mask_value(int p_layer_number, bool p_value); + bool get_collision_mask_value(int p_layer_number) const; void set_disable_mode(DisableMode p_mode); DisableMode get_disable_mode() const; diff --git a/scene/3d/visual_instance_3d.cpp b/scene/3d/visual_instance_3d.cpp index c155819159..b437379b2a 100644 --- a/scene/3d/visual_instance_3d.cpp +++ b/scene/3d/visual_instance_3d.cpp @@ -93,18 +93,22 @@ uint32_t VisualInstance3D::get_layer_mask() const { return layers; } -void VisualInstance3D::set_layer_mask_bit(int p_layer, bool p_enable) { - ERR_FAIL_INDEX(p_layer, 32); - if (p_enable) { - set_layer_mask(layers | (1 << p_layer)); +void VisualInstance3D::set_layer_mask_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Render layer number must be between 1 and 20 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 20, "Render layer number must be between 1 and 20 inclusive."); + uint32_t mask = get_layer_mask(); + if (p_value) { + mask |= 1 << (p_layer_number - 1); } else { - set_layer_mask(layers & (~(1 << p_layer))); + mask &= ~(1 << (p_layer_number - 1)); } + set_layer_mask(mask); } -bool VisualInstance3D::get_layer_mask_bit(int p_layer) const { - ERR_FAIL_INDEX_V(p_layer, 32, false); - return (layers & (1 << p_layer)); +bool VisualInstance3D::get_layer_mask_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Render layer number must be between 1 and 20 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 20, false, "Render layer number must be between 1 and 20 inclusive."); + return layers & (1 << (p_layer_number - 1)); } void VisualInstance3D::_bind_methods() { @@ -114,8 +118,8 @@ void VisualInstance3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_instance"), &VisualInstance3D::get_instance); ClassDB::bind_method(D_METHOD("set_layer_mask", "mask"), &VisualInstance3D::set_layer_mask); ClassDB::bind_method(D_METHOD("get_layer_mask"), &VisualInstance3D::get_layer_mask); - ClassDB::bind_method(D_METHOD("set_layer_mask_bit", "layer", "enabled"), &VisualInstance3D::set_layer_mask_bit); - ClassDB::bind_method(D_METHOD("get_layer_mask_bit", "layer"), &VisualInstance3D::get_layer_mask_bit); + ClassDB::bind_method(D_METHOD("set_layer_mask_value", "layer_number", "value"), &VisualInstance3D::set_layer_mask_value); + ClassDB::bind_method(D_METHOD("get_layer_mask_value", "layer_number"), &VisualInstance3D::get_layer_mask_value); ClassDB::bind_method(D_METHOD("get_transformed_aabb"), &VisualInstance3D::get_transformed_aabb); diff --git a/scene/3d/visual_instance_3d.h b/scene/3d/visual_instance_3d.h index 97aac149a1..aa64195c2b 100644 --- a/scene/3d/visual_instance_3d.h +++ b/scene/3d/visual_instance_3d.h @@ -72,8 +72,8 @@ public: void set_layer_mask(uint32_t p_mask); uint32_t get_layer_mask() const; - void set_layer_mask_bit(int p_layer, bool p_enable); - bool get_layer_mask_bit(int p_layer) const; + void set_layer_mask_value(int p_layer_number, bool p_enable); + bool get_layer_mask_value(int p_layer_number) const; VisualInstance3D(); ~VisualInstance3D(); diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 5ff92c6ff6..04a533bedb 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -994,15 +994,15 @@ void register_scene_types() { OS::get_singleton()->yield(); //may take time to init for (int i = 0; i < 20; i++) { - GLOBAL_DEF_BASIC(vformat("layer_names/2d_render/layer_%d", i), ""); - GLOBAL_DEF_BASIC(vformat("layer_names/3d_render/layer_%d", i), ""); + GLOBAL_DEF_BASIC(vformat("layer_names/2d_render/layer_%d", i + 1), ""); + GLOBAL_DEF_BASIC(vformat("layer_names/3d_render/layer_%d", i + 1), ""); } for (int i = 0; i < 32; i++) { - GLOBAL_DEF_BASIC(vformat("layer_names/2d_physics/layer_%d", i), ""); - GLOBAL_DEF_BASIC(vformat("layer_names/2d_navigation/layer_%d", i), ""); - GLOBAL_DEF_BASIC(vformat("layer_names/3d_physics/layer_%d", i), ""); - GLOBAL_DEF_BASIC(vformat("layer_names/3d_navigation/layer_%d", i), ""); + GLOBAL_DEF_BASIC(vformat("layer_names/2d_physics/layer_%d", i + 1), ""); + GLOBAL_DEF_BASIC(vformat("layer_names/2d_navigation/layer_%d", i + 1), ""); + GLOBAL_DEF_BASIC(vformat("layer_names/3d_physics/layer_%d", i + 1), ""); + GLOBAL_DEF_BASIC(vformat("layer_names/3d_navigation/layer_%d", i + 1), ""); } bool default_theme_hidpi = GLOBAL_DEF("gui/theme/use_hidpi", false); diff --git a/scene/resources/navigation_mesh.cpp b/scene/resources/navigation_mesh.cpp index 3c3c643367..00cee9269b 100644 --- a/scene/resources/navigation_mesh.cpp +++ b/scene/resources/navigation_mesh.cpp @@ -91,20 +91,22 @@ uint32_t NavigationMesh::get_collision_mask() const { return collision_mask; } -void NavigationMesh::set_collision_mask_bit(int p_bit, bool p_value) { - ERR_FAIL_INDEX_MSG(p_bit, 32, "Collision mask bit must be between 0 and 31 inclusive."); +void NavigationMesh::set_collision_mask_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive."); uint32_t mask = get_collision_mask(); if (p_value) { - mask |= 1 << p_bit; + mask |= 1 << (p_layer_number - 1); } else { - mask &= ~(1 << p_bit); + mask &= ~(1 << (p_layer_number - 1)); } set_collision_mask(mask); } -bool NavigationMesh::get_collision_mask_bit(int p_bit) const { - ERR_FAIL_INDEX_V_MSG(p_bit, 32, false, "Collision mask bit must be between 0 and 31 inclusive."); - return get_collision_mask() & (1 << p_bit); +bool NavigationMesh::get_collision_mask_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive."); + return get_collision_mask() & (1 << (p_layer_number - 1)); } void NavigationMesh::set_source_geometry_mode(SourceGeometryMode p_geometry_mode) { @@ -403,8 +405,8 @@ void NavigationMesh::_bind_methods() { ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &NavigationMesh::set_collision_mask); ClassDB::bind_method(D_METHOD("get_collision_mask"), &NavigationMesh::get_collision_mask); - ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &NavigationMesh::set_collision_mask_bit); - ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &NavigationMesh::get_collision_mask_bit); + ClassDB::bind_method(D_METHOD("set_collision_mask_value", "layer_number", "value"), &NavigationMesh::set_collision_mask_value); + ClassDB::bind_method(D_METHOD("get_collision_mask_value", "layer_number"), &NavigationMesh::get_collision_mask_value); ClassDB::bind_method(D_METHOD("set_source_geometry_mode", "mask"), &NavigationMesh::set_source_geometry_mode); ClassDB::bind_method(D_METHOD("get_source_geometry_mode"), &NavigationMesh::get_source_geometry_mode); diff --git a/scene/resources/navigation_mesh.h b/scene/resources/navigation_mesh.h index 99b2b6ff58..1cdf7a07ed 100644 --- a/scene/resources/navigation_mesh.h +++ b/scene/resources/navigation_mesh.h @@ -118,8 +118,8 @@ public: void set_collision_mask(uint32_t p_mask); uint32_t get_collision_mask() const; - void set_collision_mask_bit(int p_bit, bool p_value); - bool get_collision_mask_bit(int p_bit) const; + void set_collision_mask_value(int p_layer_number, bool p_value); + bool get_collision_mask_value(int p_layer_number) const; void set_source_geometry_mode(SourceGeometryMode p_geometry_mode); SourceGeometryMode get_source_geometry_mode() const; diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index a6815da6f4..75dd7448e7 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -3081,10 +3081,84 @@ String VisualShaderNodeUniform::get_warning(Shader::Mode p_mode, VisualShader::T List<String> keyword_list; ShaderLanguage::get_keyword_list(&keyword_list); if (keyword_list.find(uniform_name)) { - return TTR("Uniform name cannot be equal to a shader keyword. Choose another name."); + return TTR("Shader keywords cannot be used as uniform names.\nChoose another name."); } if (!is_qualifier_supported(qualifier)) { - return "This uniform type does not support that qualifier."; + String qualifier_str; + switch (qualifier) { + case QUAL_NONE: + break; + case QUAL_GLOBAL: + qualifier_str = "global"; + break; + case QUAL_INSTANCE: + qualifier_str = "instance"; + break; + } + return vformat(TTR("This uniform type does not support the '%s' qualifier."), qualifier_str); + } else if (qualifier == Qualifier::QUAL_GLOBAL) { + RS::GlobalVariableType gvt = RS::get_singleton()->global_variable_get_type(uniform_name); + if (gvt == RS::GLOBAL_VAR_TYPE_MAX) { + return vformat(TTR("Global uniform '%s' does not exist.\nCreate it in the Project Settings."), uniform_name); + } + bool incompatible_type = false; + switch (gvt) { + case RS::GLOBAL_VAR_TYPE_FLOAT: { + if (!Object::cast_to<VisualShaderNodeFloatUniform>(this)) { + incompatible_type = true; + } + } break; + case RS::GLOBAL_VAR_TYPE_INT: { + if (!Object::cast_to<VisualShaderNodeIntUniform>(this)) { + incompatible_type = true; + } + } break; + case RS::GLOBAL_VAR_TYPE_BOOL: { + if (!Object::cast_to<VisualShaderNodeBooleanUniform>(this)) { + incompatible_type = true; + } + } break; + case RS::GLOBAL_VAR_TYPE_COLOR: { + if (!Object::cast_to<VisualShaderNodeColorUniform>(this)) { + incompatible_type = true; + } + } break; + case RS::GLOBAL_VAR_TYPE_VEC3: { + if (!Object::cast_to<VisualShaderNodeVec3Uniform>(this)) { + incompatible_type = true; + } + } break; + case RS::GLOBAL_VAR_TYPE_TRANSFORM: { + if (!Object::cast_to<VisualShaderNodeTransformUniform>(this)) { + incompatible_type = true; + } + } break; + case RS::GLOBAL_VAR_TYPE_SAMPLER2D: { + if (!Object::cast_to<VisualShaderNodeTextureUniform>(this)) { + incompatible_type = true; + } + } break; + case RS::GLOBAL_VAR_TYPE_SAMPLER3D: { + if (!Object::cast_to<VisualShaderNodeTexture3DUniform>(this)) { + incompatible_type = true; + } + } break; + case RS::GLOBAL_VAR_TYPE_SAMPLER2DARRAY: { + if (!Object::cast_to<VisualShaderNodeTexture2DArrayUniform>(this)) { + incompatible_type = true; + } + } break; + case RS::GLOBAL_VAR_TYPE_SAMPLERCUBE: { + if (!Object::cast_to<VisualShaderNodeCubemapUniform>(this)) { + incompatible_type = true; + } + } break; + default: + break; + } + if (incompatible_type) { + return vformat(TTR("Global uniform '%s' has an incompatible type for this kind of node.\nChange it in the Project Settings."), uniform_name); + } } return String(); diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp index 6fd6fd8f3b..afe0bdfd5c 100644 --- a/scene/resources/visual_shader_nodes.cpp +++ b/scene/resources/visual_shader_nodes.cpp @@ -4575,7 +4575,10 @@ bool VisualShaderNodeTransformUniform::is_use_prop_slots() const { } bool VisualShaderNodeTransformUniform::is_qualifier_supported(Qualifier p_qual) const { - return true; // all qualifiers are supported + if (p_qual == Qualifier::QUAL_INSTANCE) { + return false; + } + return true; } bool VisualShaderNodeTransformUniform::is_convertible_to_constant() const { diff --git a/servers/physics_server_2d.cpp b/servers/physics_server_2d.cpp index 3efa16fe0a..c2dedde0be 100644 --- a/servers/physics_server_2d.cpp +++ b/servers/physics_server_2d.cpp @@ -234,8 +234,8 @@ void PhysicsShapeQueryParameters2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_margin", "margin"), &PhysicsShapeQueryParameters2D::set_margin); ClassDB::bind_method(D_METHOD("get_margin"), &PhysicsShapeQueryParameters2D::get_margin); - ClassDB::bind_method(D_METHOD("set_collision_layer", "collision_layer"), &PhysicsShapeQueryParameters2D::set_collision_mask); - ClassDB::bind_method(D_METHOD("get_collision_layer"), &PhysicsShapeQueryParameters2D::get_collision_mask); + ClassDB::bind_method(D_METHOD("set_collision_mask", "collision_mask"), &PhysicsShapeQueryParameters2D::set_collision_mask); + ClassDB::bind_method(D_METHOD("get_collision_mask"), &PhysicsShapeQueryParameters2D::get_collision_mask); ClassDB::bind_method(D_METHOD("set_exclude", "exclude"), &PhysicsShapeQueryParameters2D::set_exclude); ClassDB::bind_method(D_METHOD("get_exclude"), &PhysicsShapeQueryParameters2D::get_exclude); @@ -246,7 +246,7 @@ void PhysicsShapeQueryParameters2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_collide_with_areas", "enable"), &PhysicsShapeQueryParameters2D::set_collide_with_areas); ClassDB::bind_method(D_METHOD("is_collide_with_areas_enabled"), &PhysicsShapeQueryParameters2D::is_collide_with_areas_enabled); - ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_collision_layer", "get_collision_layer"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_collision_mask", "get_collision_mask"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "exclude", PROPERTY_HINT_NONE, itos(Variant::RID) + ":"), "set_exclude", "get_exclude"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "margin", PROPERTY_HINT_RANGE, "0,100,0.01"), "set_margin", "get_margin"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "motion"), "set_motion", "get_motion"); @@ -411,9 +411,9 @@ PhysicsDirectSpaceState2D::PhysicsDirectSpaceState2D() { } void PhysicsDirectSpaceState2D::_bind_methods() { - ClassDB::bind_method(D_METHOD("intersect_point", "point", "max_results", "exclude", "collision_layer", "collide_with_bodies", "collide_with_areas"), &PhysicsDirectSpaceState2D::_intersect_point, DEFVAL(32), DEFVAL(Array()), DEFVAL(0x7FFFFFFF), DEFVAL(true), DEFVAL(false)); - ClassDB::bind_method(D_METHOD("intersect_point_on_canvas", "point", "canvas_instance_id", "max_results", "exclude", "collision_layer", "collide_with_bodies", "collide_with_areas"), &PhysicsDirectSpaceState2D::_intersect_point_on_canvas, DEFVAL(32), DEFVAL(Array()), DEFVAL(0x7FFFFFFF), DEFVAL(true), DEFVAL(false)); - ClassDB::bind_method(D_METHOD("intersect_ray", "from", "to", "exclude", "collision_layer", "collide_with_bodies", "collide_with_areas"), &PhysicsDirectSpaceState2D::_intersect_ray, DEFVAL(Array()), DEFVAL(0x7FFFFFFF), DEFVAL(true), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("intersect_point", "point", "max_results", "exclude", "collision_mask", "collide_with_bodies", "collide_with_areas"), &PhysicsDirectSpaceState2D::_intersect_point, DEFVAL(32), DEFVAL(Array()), DEFVAL(0x7FFFFFFF), DEFVAL(true), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("intersect_point_on_canvas", "point", "canvas_instance_id", "max_results", "exclude", "collision_mask", "collide_with_bodies", "collide_with_areas"), &PhysicsDirectSpaceState2D::_intersect_point_on_canvas, DEFVAL(32), DEFVAL(Array()), DEFVAL(0x7FFFFFFF), DEFVAL(true), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("intersect_ray", "from", "to", "exclude", "collision_mask", "collide_with_bodies", "collide_with_areas"), &PhysicsDirectSpaceState2D::_intersect_ray, DEFVAL(Array()), DEFVAL(0x7FFFFFFF), DEFVAL(true), DEFVAL(false)); ClassDB::bind_method(D_METHOD("intersect_shape", "shape", "max_results"), &PhysicsDirectSpaceState2D::_intersect_shape, DEFVAL(32)); ClassDB::bind_method(D_METHOD("cast_motion", "shape"), &PhysicsDirectSpaceState2D::_cast_motion); ClassDB::bind_method(D_METHOD("collide_shape", "shape", "max_results"), &PhysicsDirectSpaceState2D::_collide_shape, DEFVAL(32)); diff --git a/servers/rendering/renderer_rd/renderer_storage_rd.cpp b/servers/rendering/renderer_rd/renderer_storage_rd.cpp index 4feaf08808..475f6c475d 100644 --- a/servers/rendering/renderer_rd/renderer_storage_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_storage_rd.cpp @@ -9129,7 +9129,7 @@ RendererStorageRD::RendererStorageRD() { } break; case RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS: { sampler_state.mag_filter = RD::SAMPLER_FILTER_NEAREST; - sampler_state.min_filter = RD::SAMPLER_FILTER_LINEAR; + sampler_state.min_filter = RD::SAMPLER_FILTER_NEAREST; if (GLOBAL_GET("rendering/textures/default_filters/use_nearest_mipmap_filter")) { sampler_state.mip_filter = RD::SAMPLER_FILTER_NEAREST; } else { @@ -9148,7 +9148,7 @@ RendererStorageRD::RendererStorageRD() { } break; case RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC: { sampler_state.mag_filter = RD::SAMPLER_FILTER_NEAREST; - sampler_state.min_filter = RD::SAMPLER_FILTER_LINEAR; + sampler_state.min_filter = RD::SAMPLER_FILTER_NEAREST; if (GLOBAL_GET("rendering/textures/default_filters/use_nearest_mipmap_filter")) { sampler_state.mip_filter = RD::SAMPLER_FILTER_NEAREST; } else { |