diff --git a/docs/binary.md b/docs/binary.md index 47fa11ba9..a11cbc3ca 100644 --- a/docs/binary.md +++ b/docs/binary.md @@ -50,6 +50,7 @@ This document is based on: - [SharedString](#sharedstring) - [OptionalCoordinateFrame](#optionalcoordinateframe) - [UniqueId](#uniqueid) + - [Font](#font) - [Data Storage Notes](#data-storage-notes) - [Integer Transformations](#integer-transformations) - [Byte Interleaving](#byte-interleaving) @@ -639,6 +640,21 @@ When interacting with the XML format, care must be taken because `UniqueId` is s When an array of `UniqueId` values is present, the bytes are subject to [byte interleaving](#byte-interleaving). +### Font +**Type ID `0x20`** + +The `Font` type is a struct composed of two `String` values, a `u8`, and a `u16`: + +| Field Name | Format | Value | +|:-------------|:--------------------|:---------------------------------------| +| Family | [`String`](#string) | The font family content URI | +| Weight | `u16` | The weight of the font | +| Style | `u8` | The style of the font | +| CachedFaceId | [`String`](#string) | The cached content URI of the TTF file | + +The `Weight` and `Style` fields are stored as little-endian unsigned integers. These are usually treated like enums, and to assign them in Roblox Studio an Enum is used. Interestingly, the `Weight` is *always* stored as a number in binary and XML, but `Style` is stored as a number in binary and as text in XML. + +The `CachedFaceId` field is always present, but is allowed to be an empty string (a string of length `0`). When represented in XML, this property will be omitted if it is an empty string. This property is not visible via any user APIs in Roblox Studio. ## Data Storage Notes diff --git a/generate_reflection/src/api_dump.rs b/generate_reflection/src/api_dump.rs index 2d002984d..f4c55b158 100644 --- a/generate_reflection/src/api_dump.rs +++ b/generate_reflection/src/api_dump.rs @@ -269,6 +269,7 @@ fn variant_type_from_str(value: &str) -> Option { "ColorSequence" => VariantType::ColorSequence, "Content" => VariantType::Content, "Faces" => VariantType::Faces, + "Font" => VariantType::Font, "Instance" => VariantType::Ref, "NumberRange" => VariantType::NumberRange, "NumberSequence" => VariantType::NumberSequence, @@ -296,9 +297,6 @@ fn variant_type_from_str(value: &str) -> Option { // TweenInfo is not supported by rbx_types yet "TweenInfo" => return None, - // Font is not supported by rbx_types yet - "Font" => return None, - // While DateTime is possible to Serialize, the only use it has as a // DataType is for the TextChatMessage class, which cannot be serialized // (at least not saved to file as it is locked to nil parent) diff --git a/generate_reflection/src/values.rs b/generate_reflection/src/values.rs index d0d1a5d48..7d2b374e3 100644 --- a/generate_reflection/src/values.rs +++ b/generate_reflection/src/values.rs @@ -4,9 +4,10 @@ use std::collections::BTreeMap; use rbx_dom_weak::types::{ Attributes, Axes, BinaryString, BrickColor, CFrame, Color3, Color3uint8, ColorSequence, - ColorSequenceKeypoint, Content, CustomPhysicalProperties, Enum, Faces, Matrix3, NumberRange, - NumberSequence, NumberSequenceKeypoint, PhysicalProperties, Ray, Rect, Region3int16, Tags, - UDim, UDim2, Variant, VariantType, Vector2, Vector2int16, Vector3, Vector3int16, + ColorSequenceKeypoint, Content, CustomPhysicalProperties, Enum, Faces, Font, Matrix3, + NumberRange, NumberSequence, NumberSequenceKeypoint, PhysicalProperties, Ray, Rect, + Region3int16, Tags, UDim, UDim2, Variant, VariantType, Vector2, Vector2int16, Vector3, + Vector3int16, }; use serde::Serialize; @@ -77,6 +78,7 @@ pub fn encode() -> anyhow::Result { values.insert("Faces", Faces::all().into()); values.insert("Float32", 15.0f32.into()); values.insert("Float64", 15123.0f64.into()); + values.insert("Font", Font::default().into()); values.insert("Int32", 6014i32.into()); values.insert("Int64", 23491023i64.into()); values.insert("NumberRange", NumberRange::new(-36.0, 94.0).into()); diff --git a/rbx_binary/CHANGELOG.md b/rbx_binary/CHANGELOG.md index d08404f1c..f7dedb07f 100644 --- a/rbx_binary/CHANGELOG.md +++ b/rbx_binary/CHANGELOG.md @@ -1,6 +1,9 @@ # rbx_binary Changelog ## Unreleased +* Added support for `Font` values. ([#248]) + +[#248]: https://github.com/rojo-rbx/rbx-dom/pull/248 ## 0.6.6 (2022-06-29) * Fixed unserialized properties getting deserialized, like `BasePart.MaterialVariant`. ([#230]) diff --git a/rbx_binary/src/deserializer/state.rs b/rbx_binary/src/deserializer/state.rs index 10677f455..ff428cdcb 100644 --- a/rbx_binary/src/deserializer/state.rs +++ b/rbx_binary/src/deserializer/state.rs @@ -7,9 +7,10 @@ use std::{ use rbx_dom_weak::{ types::{ Attributes, Axes, BinaryString, BrickColor, CFrame, Color3, Color3uint8, ColorSequence, - ColorSequenceKeypoint, Content, CustomPhysicalProperties, Enum, Faces, Matrix3, - NumberRange, NumberSequence, NumberSequenceKeypoint, PhysicalProperties, Ray, Rect, Ref, - SharedString, Tags, UDim, UDim2, Variant, VariantType, Vector2, Vector3, Vector3int16, + ColorSequenceKeypoint, Content, CustomPhysicalProperties, Enum, Faces, Font, FontStyle, + FontWeight, Matrix3, NumberRange, NumberSequence, NumberSequenceKeypoint, + PhysicalProperties, Ray, Rect, Ref, SharedString, Tags, UDim, UDim2, Variant, VariantType, + Vector2, Vector3, Vector3int16, }, InstanceBuilder, WeakDom, }; @@ -863,6 +864,42 @@ impl<'a, R: Read> DeserializerState<'a, R> { }); } }, + Type::Font => match canonical_type { + VariantType::Font => { + for referent in &type_info.referents { + let instance = self.instances_by_ref.get_mut(referent).unwrap(); + + let family = chunk.read_string()?; + let weight = FontWeight::from_u16(chunk.read_le_u16()?); + let style = FontStyle::from_u8(chunk.read_u8()?); + let cached_face_id = chunk.read_string()?; + + let cached_face_id = if cached_face_id.is_empty() { + None + } else { + Some(cached_face_id) + }; + + instance.builder.add_property( + &canonical_name, + Font { + family, + weight, + style, + cached_face_id, + }, + ); + } + } + invalid_type => { + return Err(InnerError::PropTypeMismatch { + type_name: type_info.type_name.clone(), + prop_name, + valid_type_names: "Font", + actual_type_name: format!("{:?}", invalid_type), + }); + } + }, Type::NumberSequence => match canonical_type { VariantType::NumberSequence => { for referent in &type_info.referents { diff --git a/rbx_binary/src/serializer/state.rs b/rbx_binary/src/serializer/state.rs index 0236c82fa..042cdce52 100644 --- a/rbx_binary/src/serializer/state.rs +++ b/rbx_binary/src/serializer/state.rs @@ -9,7 +9,7 @@ use std::{ use rbx_dom_weak::{ types::{ Attributes, Axes, BinaryString, BrickColor, CFrame, Color3, Color3uint8, ColorSequence, - ColorSequenceKeypoint, Content, Enum, Faces, Matrix3, NumberRange, NumberSequence, + ColorSequenceKeypoint, Content, Enum, Faces, Font, Matrix3, NumberRange, NumberSequence, NumberSequenceKeypoint, PhysicalProperties, Ray, Rect, Ref, SharedString, Tags, UDim, UDim2, Variant, VariantType, Vector2, Vector3, Vector3int16, }, @@ -709,6 +709,20 @@ impl<'dom, W: Write> SerializerState<'dom, W> { chunk.write_interleaved_i32_array(offset_x.into_iter())?; chunk.write_interleaved_i32_array(offset_y.into_iter())?; } + Type::Font => { + for (i, rbx_value) in values { + if let Variant::Font(value) = rbx_value.as_ref() { + chunk.write_string(&value.family)?; + chunk.write_le_u16(value.weight.as_u16())?; + chunk.write_u8(value.style.as_u8())?; + chunk.write_string( + &value.cached_face_id.clone().unwrap_or_default(), + )?; + } else { + return type_mismatch(i, &rbx_value, "Font"); + } + } + } Type::Ray => { for (i, rbx_value) in values { if let Variant::Ray(value) = rbx_value.as_ref() { @@ -1229,6 +1243,7 @@ impl<'dom, W: Write> SerializerState<'dom, W> { VariantType::Tags => Variant::Tags(Tags::new()), VariantType::Content => Variant::Content(Content::new()), VariantType::Attributes => Variant::Attributes(Attributes::new()), + VariantType::Font => Variant::Font(Font::default()), _ => return None, }) } diff --git a/rbx_binary/src/tests/models.rs b/rbx_binary/src/tests/models.rs index b6feb5aa0..c2e8719e4 100644 --- a/rbx_binary/src/tests/models.rs +++ b/rbx_binary/src/tests/models.rs @@ -59,4 +59,5 @@ binary_tests! { two_terrainregions, weldconstraint, package_link, + text_label_with_font, } diff --git a/rbx_binary/src/tests/snapshots/rbx_binary__tests__util__text-label-with-font__decoded.snap b/rbx_binary/src/tests/snapshots/rbx_binary__tests__util__text-label-with-font__decoded.snap new file mode 100644 index 000000000..98e646be1 --- /dev/null +++ b/rbx_binary/src/tests/snapshots/rbx_binary__tests__util__text-label-with-font__decoded.snap @@ -0,0 +1,129 @@ +--- +source: rbx_binary/src/tests/util.rs +assertion_line: 33 +expression: decoded_viewed +--- +- referent: referent-0 + name: TextLabel + class: TextLabel + properties: + Active: + Bool: false + AnchorPoint: + Vector2: + - 0 + - 0 + Attributes: + Attributes: {} + AutoLocalize: + Bool: true + AutomaticSize: + Enum: 0 + BackgroundColor3: + Color3: + - 1 + - 1 + - 1 + BackgroundTransparency: + Float32: 0 + BorderColor3: + Color3: + - 0.10588236 + - 0.16470589 + - 0.20784315 + BorderMode: + Enum: 0 + BorderSizePixel: + Int32: 1 + ClipsDescendants: + Bool: false + Draggable: + Bool: false + FontFace: + Font: + family: "rbxasset://fonts/families/RobotoMono.json" + weight: Bold + style: Italic + cachedFaceId: ~ + LayoutOrder: + Int32: 0 + LineHeight: + Float32: 1 + MaxVisibleGraphemes: + Int32: -1 + NextSelectionDown: "null" + NextSelectionLeft: "null" + NextSelectionRight: "null" + NextSelectionUp: "null" + Position: + UDim2: + - - 0 + - 0 + - - 0 + - 0 + RichText: + Bool: false + RootLocalizationTable: "null" + Rotation: + Float32: 0 + Selectable: + Bool: false + SelectionBehaviorDown: + Enum: 0 + SelectionBehaviorLeft: + Enum: 0 + SelectionBehaviorRight: + Enum: 0 + SelectionBehaviorUp: + Enum: 0 + SelectionGroup: + Bool: false + SelectionImageObject: "null" + SelectionOrder: + Int32: 0 + Size: + UDim2: + - - 0 + - 200 + - - 0 + - 50 + SizeConstraint: + Enum: 0 + SourceAssetId: + Int64: -1 + Tags: + Tags: [] + Text: + String: My Text + TextColor3: + Color3: + - 0 + - 0 + - 0 + TextScaled: + Bool: false + TextSize: + Float32: 14 + TextStrokeColor3: + Color3: + - 0 + - 0 + - 0 + TextStrokeTransparency: + Float32: 1 + TextTransparency: + Float32: 0 + TextTruncate: + Enum: 0 + TextWrapped: + Bool: false + TextXAlignment: + Enum: 2 + TextYAlignment: + Enum: 1 + Visible: + Bool: true + ZIndex: + Int32: 1 + children: [] + diff --git a/rbx_binary/src/tests/snapshots/rbx_binary__tests__util__text-label-with-font__encoded.snap b/rbx_binary/src/tests/snapshots/rbx_binary__tests__util__text-label-with-font__encoded.snap new file mode 100644 index 000000000..7f2970ec4 --- /dev/null +++ b/rbx_binary/src/tests/snapshots/rbx_binary__tests__util__text-label-with-font__encoded.snap @@ -0,0 +1,339 @@ +--- +source: rbx_binary/src/tests/util.rs +assertion_line: 46 +expression: text_roundtrip +--- +num_types: 1 +num_instances: 1 +chunks: + - Inst: + type_id: 0 + type_name: TextLabel + object_format: 0 + referents: + - 0 + - Prop: + type_id: 0 + prop_name: Active + prop_type: Bool + values: + - false + - Prop: + type_id: 0 + prop_name: AnchorPoint + prop_type: Vector2 + values: + - - 0 + - 0 + - Prop: + type_id: 0 + prop_name: AttributesSerialize + prop_type: String + values: + - "" + - Prop: + type_id: 0 + prop_name: AutoLocalize + prop_type: Bool + values: + - true + - Prop: + type_id: 0 + prop_name: AutomaticSize + prop_type: Enum + values: + - 0 + - Prop: + type_id: 0 + prop_name: BackgroundColor3 + prop_type: Color3 + values: + - - 1 + - 1 + - 1 + - Prop: + type_id: 0 + prop_name: BackgroundTransparency + prop_type: Float32 + values: + - 0 + - Prop: + type_id: 0 + prop_name: BorderColor3 + prop_type: Color3 + values: + - - 0.10588236 + - 0.16470589 + - 0.20784315 + - Prop: + type_id: 0 + prop_name: BorderMode + prop_type: Enum + values: + - 0 + - Prop: + type_id: 0 + prop_name: BorderSizePixel + prop_type: Int32 + values: + - 1 + - Prop: + type_id: 0 + prop_name: ClipsDescendants + prop_type: Bool + values: + - false + - Prop: + type_id: 0 + prop_name: Draggable + prop_type: Bool + values: + - false + - Prop: + type_id: 0 + prop_name: FontFace + prop_type: Font + values: + - family: "rbxasset://fonts/families/RobotoMono.json" + weight: Bold + style: Italic + cachedFaceId: ~ + - Prop: + type_id: 0 + prop_name: LayoutOrder + prop_type: Int32 + values: + - 0 + - Prop: + type_id: 0 + prop_name: LineHeight + prop_type: Float32 + values: + - 1 + - Prop: + type_id: 0 + prop_name: MaxVisibleGraphemes + prop_type: Int32 + values: + - -1 + - Prop: + type_id: 0 + prop_name: Name + prop_type: String + values: + - TextLabel + - Prop: + type_id: 0 + prop_name: NextSelectionDown + prop_type: Ref + values: + - -1 + - Prop: + type_id: 0 + prop_name: NextSelectionLeft + prop_type: Ref + values: + - -1 + - Prop: + type_id: 0 + prop_name: NextSelectionRight + prop_type: Ref + values: + - -1 + - Prop: + type_id: 0 + prop_name: NextSelectionUp + prop_type: Ref + values: + - -1 + - Prop: + type_id: 0 + prop_name: Position + prop_type: UDim2 + values: + - - - 0 + - 0 + - - 0 + - 0 + - Prop: + type_id: 0 + prop_name: RichText + prop_type: Bool + values: + - false + - Prop: + type_id: 0 + prop_name: RootLocalizationTable + prop_type: Ref + values: + - -1 + - Prop: + type_id: 0 + prop_name: Rotation + prop_type: Float32 + values: + - 0 + - Prop: + type_id: 0 + prop_name: Selectable + prop_type: Bool + values: + - false + - Prop: + type_id: 0 + prop_name: SelectionBehaviorDown + prop_type: Enum + values: + - 0 + - Prop: + type_id: 0 + prop_name: SelectionBehaviorLeft + prop_type: Enum + values: + - 0 + - Prop: + type_id: 0 + prop_name: SelectionBehaviorRight + prop_type: Enum + values: + - 0 + - Prop: + type_id: 0 + prop_name: SelectionBehaviorUp + prop_type: Enum + values: + - 0 + - Prop: + type_id: 0 + prop_name: SelectionGroup + prop_type: Bool + values: + - false + - Prop: + type_id: 0 + prop_name: SelectionImageObject + prop_type: Ref + values: + - -1 + - Prop: + type_id: 0 + prop_name: SelectionOrder + prop_type: Int32 + values: + - 0 + - Prop: + type_id: 0 + prop_name: Size + prop_type: UDim2 + values: + - - - 0 + - 200 + - - 0 + - 50 + - Prop: + type_id: 0 + prop_name: SizeConstraint + prop_type: Enum + values: + - 0 + - Prop: + type_id: 0 + prop_name: SourceAssetId + prop_type: Int64 + values: + - -1 + - Prop: + type_id: 0 + prop_name: Tags + prop_type: String + values: + - "" + - Prop: + type_id: 0 + prop_name: Text + prop_type: String + values: + - My Text + - Prop: + type_id: 0 + prop_name: TextColor3 + prop_type: Color3 + values: + - - 0 + - 0 + - 0 + - Prop: + type_id: 0 + prop_name: TextScaled + prop_type: Bool + values: + - false + - Prop: + type_id: 0 + prop_name: TextSize + prop_type: Float32 + values: + - 14 + - Prop: + type_id: 0 + prop_name: TextStrokeColor3 + prop_type: Color3 + values: + - - 0 + - 0 + - 0 + - Prop: + type_id: 0 + prop_name: TextStrokeTransparency + prop_type: Float32 + values: + - 1 + - Prop: + type_id: 0 + prop_name: TextTransparency + prop_type: Float32 + values: + - 0 + - Prop: + type_id: 0 + prop_name: TextTruncate + prop_type: Enum + values: + - 0 + - Prop: + type_id: 0 + prop_name: TextWrapped + prop_type: Bool + values: + - false + - Prop: + type_id: 0 + prop_name: TextXAlignment + prop_type: Enum + values: + - 2 + - Prop: + type_id: 0 + prop_name: TextYAlignment + prop_type: Enum + values: + - 1 + - Prop: + type_id: 0 + prop_name: Visible + prop_type: Bool + values: + - true + - Prop: + type_id: 0 + prop_name: ZIndex + prop_type: Int32 + values: + - 1 + - Prnt: + version: 0 + links: + - - 0 + - -1 + - End + diff --git a/rbx_binary/src/tests/snapshots/rbx_binary__tests__util__text-label-with-font__input.snap b/rbx_binary/src/tests/snapshots/rbx_binary__tests__util__text-label-with-font__input.snap new file mode 100644 index 000000000..474b1d532 --- /dev/null +++ b/rbx_binary/src/tests/snapshots/rbx_binary__tests__util__text-label-with-font__input.snap @@ -0,0 +1,343 @@ +--- +source: rbx_binary/src/tests/util.rs +assertion_line: 27 +expression: text_decoded +--- +num_types: 1 +num_instances: 1 +chunks: + - Meta: + entries: + - - ExplicitAutoJoints + - "true" + - Inst: + type_id: 0 + type_name: TextLabel + object_format: 0 + referents: + - 0 + - Prop: + type_id: 0 + prop_name: Active + prop_type: Bool + values: + - false + - Prop: + type_id: 0 + prop_name: AnchorPoint + prop_type: Vector2 + values: + - - 0 + - 0 + - Prop: + type_id: 0 + prop_name: AttributesSerialize + prop_type: String + values: + - "" + - Prop: + type_id: 0 + prop_name: AutoLocalize + prop_type: Bool + values: + - true + - Prop: + type_id: 0 + prop_name: AutomaticSize + prop_type: Enum + values: + - 0 + - Prop: + type_id: 0 + prop_name: BackgroundColor3 + prop_type: Color3 + values: + - - 1 + - 1 + - 1 + - Prop: + type_id: 0 + prop_name: BackgroundTransparency + prop_type: Float32 + values: + - 0 + - Prop: + type_id: 0 + prop_name: BorderColor3 + prop_type: Color3 + values: + - - 0.10588236 + - 0.16470589 + - 0.20784315 + - Prop: + type_id: 0 + prop_name: BorderMode + prop_type: Enum + values: + - 0 + - Prop: + type_id: 0 + prop_name: BorderSizePixel + prop_type: Int32 + values: + - 1 + - Prop: + type_id: 0 + prop_name: ClipsDescendants + prop_type: Bool + values: + - false + - Prop: + type_id: 0 + prop_name: Draggable + prop_type: Bool + values: + - false + - Prop: + type_id: 0 + prop_name: FontFace + prop_type: Font + values: + - family: "rbxasset://fonts/families/RobotoMono.json" + weight: Bold + style: Italic + cachedFaceId: ~ + - Prop: + type_id: 0 + prop_name: LayoutOrder + prop_type: Int32 + values: + - 0 + - Prop: + type_id: 0 + prop_name: LineHeight + prop_type: Float32 + values: + - 1 + - Prop: + type_id: 0 + prop_name: MaxVisibleGraphemes + prop_type: Int32 + values: + - -1 + - Prop: + type_id: 0 + prop_name: Name + prop_type: String + values: + - TextLabel + - Prop: + type_id: 0 + prop_name: NextSelectionDown + prop_type: Ref + values: + - -1 + - Prop: + type_id: 0 + prop_name: NextSelectionLeft + prop_type: Ref + values: + - -1 + - Prop: + type_id: 0 + prop_name: NextSelectionRight + prop_type: Ref + values: + - -1 + - Prop: + type_id: 0 + prop_name: NextSelectionUp + prop_type: Ref + values: + - -1 + - Prop: + type_id: 0 + prop_name: Position + prop_type: UDim2 + values: + - - - 0 + - 0 + - - 0 + - 0 + - Prop: + type_id: 0 + prop_name: RichText + prop_type: Bool + values: + - false + - Prop: + type_id: 0 + prop_name: RootLocalizationTable + prop_type: Ref + values: + - -1 + - Prop: + type_id: 0 + prop_name: Rotation + prop_type: Float32 + values: + - 0 + - Prop: + type_id: 0 + prop_name: Selectable + prop_type: Bool + values: + - false + - Prop: + type_id: 0 + prop_name: SelectionBehaviorDown + prop_type: Enum + values: + - 0 + - Prop: + type_id: 0 + prop_name: SelectionBehaviorLeft + prop_type: Enum + values: + - 0 + - Prop: + type_id: 0 + prop_name: SelectionBehaviorRight + prop_type: Enum + values: + - 0 + - Prop: + type_id: 0 + prop_name: SelectionBehaviorUp + prop_type: Enum + values: + - 0 + - Prop: + type_id: 0 + prop_name: SelectionGroup + prop_type: Bool + values: + - false + - Prop: + type_id: 0 + prop_name: SelectionImageObject + prop_type: Ref + values: + - -1 + - Prop: + type_id: 0 + prop_name: SelectionOrder + prop_type: Int32 + values: + - 0 + - Prop: + type_id: 0 + prop_name: Size + prop_type: UDim2 + values: + - - - 0 + - 200 + - - 0 + - 50 + - Prop: + type_id: 0 + prop_name: SizeConstraint + prop_type: Enum + values: + - 0 + - Prop: + type_id: 0 + prop_name: SourceAssetId + prop_type: Int64 + values: + - -1 + - Prop: + type_id: 0 + prop_name: Tags + prop_type: String + values: + - "" + - Prop: + type_id: 0 + prop_name: Text + prop_type: String + values: + - My Text + - Prop: + type_id: 0 + prop_name: TextColor3 + prop_type: Color3 + values: + - - 0 + - 0 + - 0 + - Prop: + type_id: 0 + prop_name: TextScaled + prop_type: Bool + values: + - false + - Prop: + type_id: 0 + prop_name: TextSize + prop_type: Float32 + values: + - 14 + - Prop: + type_id: 0 + prop_name: TextStrokeColor3 + prop_type: Color3 + values: + - - 0 + - 0 + - 0 + - Prop: + type_id: 0 + prop_name: TextStrokeTransparency + prop_type: Float32 + values: + - 1 + - Prop: + type_id: 0 + prop_name: TextTransparency + prop_type: Float32 + values: + - 0 + - Prop: + type_id: 0 + prop_name: TextTruncate + prop_type: Enum + values: + - 0 + - Prop: + type_id: 0 + prop_name: TextWrapped + prop_type: Bool + values: + - false + - Prop: + type_id: 0 + prop_name: TextXAlignment + prop_type: Enum + values: + - 2 + - Prop: + type_id: 0 + prop_name: TextYAlignment + prop_type: Enum + values: + - 1 + - Prop: + type_id: 0 + prop_name: Visible + prop_type: Bool + values: + - true + - Prop: + type_id: 0 + prop_name: ZIndex + prop_type: Int32 + values: + - 1 + - Prnt: + version: 0 + links: + - - 0 + - -1 + - End + diff --git a/rbx_binary/src/tests/snapshots/rbx_binary__tests__util__three-screengui__decoded.snap b/rbx_binary/src/tests/snapshots/rbx_binary__tests__util__three-screengui__decoded.snap index cd8a07e4b..7ff46dc05 100644 --- a/rbx_binary/src/tests/snapshots/rbx_binary__tests__util__three-screengui__decoded.snap +++ b/rbx_binary/src/tests/snapshots/rbx_binary__tests__util__three-screengui__decoded.snap @@ -15,8 +15,6 @@ expression: decoded_viewed Int32: 0 Enabled: Bool: true - IgnoreGuiInset: - Bool: false ResetOnSpawn: Bool: true RootLocalizationTable: "null" @@ -37,8 +35,6 @@ expression: decoded_viewed Int32: 1 Enabled: Bool: true - IgnoreGuiInset: - Bool: false ResetOnSpawn: Bool: true RootLocalizationTable: "null" @@ -59,8 +55,6 @@ expression: decoded_viewed Int32: 2 Enabled: Bool: true - IgnoreGuiInset: - Bool: false ResetOnSpawn: Bool: true RootLocalizationTable: "null" diff --git a/rbx_binary/src/tests/snapshots/rbx_binary__tests__util__three-screengui__encoded.snap b/rbx_binary/src/tests/snapshots/rbx_binary__tests__util__three-screengui__encoded.snap index 001d8d4b7..170be5a56 100644 --- a/rbx_binary/src/tests/snapshots/rbx_binary__tests__util__three-screengui__encoded.snap +++ b/rbx_binary/src/tests/snapshots/rbx_binary__tests__util__three-screengui__encoded.snap @@ -46,14 +46,6 @@ chunks: - true - true - true - - Prop: - type_id: 0 - prop_name: IgnoreGuiInset - prop_type: Bool - values: - - false - - false - - false - Prop: type_id: 0 prop_name: Name diff --git a/rbx_binary/src/text_deserializer.rs b/rbx_binary/src/text_deserializer.rs index 2cd6314da..0d48beab5 100644 --- a/rbx_binary/src/text_deserializer.rs +++ b/rbx_binary/src/text_deserializer.rs @@ -8,9 +8,9 @@ use std::{collections::HashMap, convert::TryInto, fmt::Write, io::Read}; use rbx_dom_weak::types::{ Axes, BrickColor, CFrame, Color3, Color3uint8, ColorSequence, ColorSequenceKeypoint, - CustomPhysicalProperties, Enum, Faces, Matrix3, NumberRange, NumberSequence, - NumberSequenceKeypoint, PhysicalProperties, Ray, Rect, SharedString, UDim, UDim2, Vector2, - Vector3, Vector3int16, + CustomPhysicalProperties, Enum, Faces, Font, FontStyle, FontWeight, Matrix3, NumberRange, + NumberSequence, NumberSequenceKeypoint, PhysicalProperties, Ray, Rect, SharedString, UDim, + UDim2, Vector2, Vector3, Vector3int16, }; use serde::{ser::SerializeSeq, Serialize, Serializer}; @@ -222,6 +222,7 @@ pub enum DecodedValues { Int64(Vec), SharedString(Vec), // For the text deserializer, we only show the index in the shared string array. OptionalCFrame(Vec>), + Font(Vec), } impl DecodedValues { @@ -310,6 +311,31 @@ impl DecodedValues { Some(DecodedValues::UDim2(values)) } + Type::Font => { + let mut values = Vec::with_capacity(prop_count); + + for _ in 0..prop_count { + let family = reader.read_string().unwrap(); + let weight = FontWeight::from_u16(reader.read_le_u16().unwrap()); + let style = FontStyle::from_u8(reader.read_u8().unwrap()); + let cached_face_id = reader.read_string().unwrap(); + + let cached_face_id = if cached_face_id.is_empty() { + None + } else { + Some(cached_face_id) + }; + + values.push(Font { + family, + weight, + style, + cached_face_id, + }) + } + + Some(DecodedValues::Font(values)) + } Type::Ray => { let mut values = Vec::with_capacity(prop_count); diff --git a/rbx_binary/src/types.rs b/rbx_binary/src/types.rs index 3b3e6c492..aa80fda0b 100644 --- a/rbx_binary/src/types.rs +++ b/rbx_binary/src/types.rs @@ -39,6 +39,7 @@ pub enum Type { Int64 = 0x1B, SharedString = 0x1C, OptionalCFrame = 0x1E, + Font = 0x20, } impl Type { @@ -76,6 +77,7 @@ impl Type { VariantType::Int64 => Type::Int64, VariantType::SharedString => Type::SharedString, VariantType::OptionalCFrame => Type::OptionalCFrame, + VariantType::Font => Type::Font, _ => return None, }) } @@ -111,6 +113,7 @@ impl Type { Type::Int64 => VariantType::Int64, Type::SharedString => VariantType::SharedString, Type::OptionalCFrame => VariantType::OptionalCFrame, + Type::Font => VariantType::Font, }) } } @@ -149,6 +152,7 @@ impl TryFrom for Type { 0x1B => Int64, 0x1C => SharedString, 0x1E => OptionalCFrame, + 0x20 => Font, _ => return Err(InvalidTypeError(value)), }) } diff --git a/rbx_dom_lua/src/EncodedValue.lua b/rbx_dom_lua/src/EncodedValue.lua index d910c297d..b1a82dd79 100644 --- a/rbx_dom_lua/src/EncodedValue.lua +++ b/rbx_dom_lua/src/EncodedValue.lua @@ -238,6 +238,23 @@ types = { toPod = serializeFloat, }, + Font = { + fromPod = function(pod) + return Font.new( + pod.family, + if pod.weight ~= nil then Enum.FontWeight[pod.weight] else nil, + if pod.style ~= nil then Enum.FontStyle[pod.style] else nil + ) + end, + toPod = function(roblox) + return { + family = roblox.Family, + weight = roblox.Weight.Name, + style = roblox.Style.Name, + } + end, + }, + Int32 = { fromPod = identity, toPod = identity, diff --git a/rbx_dom_lua/src/allValues.json b/rbx_dom_lua/src/allValues.json index ca62e5419..545c93063 100644 --- a/rbx_dom_lua/src/allValues.json +++ b/rbx_dom_lua/src/allValues.json @@ -207,6 +207,17 @@ }, "ty": "Float64" }, + "Font": { + "value": { + "Font": { + "family": "rbxasset://fonts/families/SourceSansPro.json", + "weight": "Regular", + "style": "Normal", + "cachedFaceId": null + } + }, + "ty": "Font" + }, "Int32": { "value": { "Int32": 6014 diff --git a/rbx_dom_lua/src/database.json b/rbx_dom_lua/src/database.json index 683b3f121..b92663e90 100644 --- a/rbx_dom_lua/src/database.json +++ b/rbx_dom_lua/src/database.json @@ -5998,6 +5998,14 @@ "Font": { "Enum": 18 }, + "FontFace": { + "Font": { + "family": "rbxasset://fonts/families/GothamSSm.json", + "weight": "Medium", + "style": "Normal", + "cachedFaceId": "rbxasset://fonts/GothamSSm-Medium.otf" + } + }, "LocalPlayerStudsOffset": { "Vector3": [ 0.0, @@ -14827,6 +14835,19 @@ ], "Superclass": "Instance", "Properties": { + "Font": { + "Name": "Font", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Font" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, "Size": { "Name": "Size", "Scriptability": "ReadWrite", diff --git a/rbx_reflection_database/CHANGELOG.md b/rbx_reflection_database/CHANGELOG.md index 284f43643..36f4866d8 100644 --- a/rbx_reflection_database/CHANGELOG.md +++ b/rbx_reflection_database/CHANGELOG.md @@ -1,6 +1,7 @@ # rbx\_reflection_database Changelog ## Unreleased Changes +* Updated to Roblox version 558. ## 0.2.5+roblox-530 * Updated to Roblox version 530. diff --git a/rbx_reflection_database/database.msgpack b/rbx_reflection_database/database.msgpack index c9253eb3c..d0dd1918a 100644 Binary files a/rbx_reflection_database/database.msgpack and b/rbx_reflection_database/database.msgpack differ diff --git a/rbx_types/CHANGELOG.md b/rbx_types/CHANGELOG.md index eed36a594..7bc81d35c 100644 --- a/rbx_types/CHANGELOG.md +++ b/rbx_types/CHANGELOG.md @@ -1,6 +1,9 @@ # rbx_types Changelog ## Unreleased Changes +* Implemented `Font`. ([#248]) + +[#248]: https://github.com/rojo-rbx/rbx-dom/pull/248 ## 1.4.2 (2022-06-12) * `Variant::String` now encodes correctly inside of `Attributes`. diff --git a/rbx_types/src/font.rs b/rbx_types/src/font.rs new file mode 100644 index 000000000..e7682d89e --- /dev/null +++ b/rbx_types/src/font.rs @@ -0,0 +1,104 @@ +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum FontWeight { + Thin, + ExtraLight, + Light, + Regular, + Medium, + SemiBold, + Bold, + ExtraBold, + Heavy, +} + +impl Default for FontWeight { + fn default() -> Self { + FontWeight::Regular + } +} + +impl FontWeight { + pub fn from_u16(weight: u16) -> Self { + match weight { + 100 => FontWeight::Thin, + 200 => FontWeight::ExtraLight, + 300 => FontWeight::Light, + 400 => FontWeight::Regular, + 500 => FontWeight::Medium, + 600 => FontWeight::SemiBold, + 700 => FontWeight::Bold, + 800 => FontWeight::ExtraBold, + 900 => FontWeight::Heavy, + _ => FontWeight::Regular, + } + } + pub fn as_u16(self) -> u16 { + match self { + FontWeight::Thin => 100, + FontWeight::ExtraLight => 200, + FontWeight::Light => 300, + FontWeight::Regular => 400, + FontWeight::Medium => 500, + FontWeight::SemiBold => 600, + FontWeight::Bold => 700, + FontWeight::ExtraBold => 800, + FontWeight::Heavy => 900, + } + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum FontStyle { + Normal, + Italic, +} + +impl Default for FontStyle { + fn default() -> Self { + FontStyle::Normal + } +} + +impl FontStyle { + pub fn from_u8(style: u8) -> Self { + match style { + 0 => FontStyle::Normal, + 1 => FontStyle::Italic, + _ => FontStyle::Normal, + } + } + + pub fn as_u8(self) -> u8 { + match self { + FontStyle::Normal => 0, + FontStyle::Italic => 1, + } + } +} + +/// A font face consisting of a typeface and other style properties. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr( + feature = "serde", + derive(serde::Serialize, serde::Deserialize), + serde(rename_all = "camelCase") +)] +pub struct Font { + pub family: String, + pub weight: FontWeight, + pub style: FontStyle, + pub cached_face_id: Option, +} + +impl Default for Font { + fn default() -> Self { + Self { + family: "rbxasset://fonts/families/SourceSansPro.json".to_owned(), + weight: FontWeight::default(), + style: FontStyle::default(), + cached_face_id: None, + } + } +} diff --git a/rbx_types/src/lib.rs b/rbx_types/src/lib.rs index 2c8f7ab6c..e75ca0ff9 100644 --- a/rbx_types/src/lib.rs +++ b/rbx_types/src/lib.rs @@ -10,6 +10,7 @@ mod brick_color; mod content; mod error; mod faces; +mod font; mod lister; mod physical_properties; mod referent; @@ -25,6 +26,7 @@ pub use brick_color::*; pub use content::*; pub use error::*; pub use faces::*; +pub use font::*; pub use physical_properties::*; pub use referent::*; pub use shared_string::*; diff --git a/rbx_types/src/variant.rs b/rbx_types/src/variant.rs index 18249d901..7acf08744 100644 --- a/rbx_types/src/variant.rs +++ b/rbx_types/src/variant.rs @@ -1,7 +1,8 @@ use crate::{ Attributes, Axes, BinaryString, BrickColor, CFrame, Color3, Color3uint8, ColorSequence, - Content, Enum, Faces, NumberRange, NumberSequence, PhysicalProperties, Ray, Rect, Ref, Region3, - Region3int16, SharedString, Tags, UDim, UDim2, Vector2, Vector2int16, Vector3, Vector3int16, + Content, Enum, Faces, Font, NumberRange, NumberSequence, PhysicalProperties, Ray, Rect, Ref, + Region3, Region3int16, SharedString, Tags, UDim, UDim2, Vector2, Vector2int16, Vector3, + Vector3int16, }; /// Reduces boilerplate from listing different values of Variant by wrapping @@ -128,6 +129,7 @@ make_variant! { OptionalCFrame(Option), Tags(Tags), Attributes(Attributes), + Font(Font), } impl From<&'_ str> for Variant { diff --git a/rbx_xml/CHANGELOG.md b/rbx_xml/CHANGELOG.md index 7e8bdac7b..6b5770448 100644 --- a/rbx_xml/CHANGELOG.md +++ b/rbx_xml/CHANGELOG.md @@ -1,6 +1,9 @@ # rbx_xml Changelog ## Unreleased +* Added support for `Font` values. ([#248]) + +[#248]: https://github.com/rojo-rbx/rbx-dom/pull/248 ## 0.12.4 (2022-06-12) * Implemented serialization and deserialization for `Attributes`. ([#219]) diff --git a/rbx_xml/src/types/font.rs b/rbx_xml/src/types/font.rs new file mode 100644 index 000000000..ba2f3c35c --- /dev/null +++ b/rbx_xml/src/types/font.rs @@ -0,0 +1,189 @@ +use std::io::{Read, Write}; + +use rbx_dom_weak::types::{Font, FontStyle, FontWeight}; + +use crate::{ + core::XmlType, + deserializer_core::{XmlEventReader, XmlReadEvent}, + error::{DecodeError, DecodeErrorKind, EncodeError}, + serializer_core::{XmlEventWriter, XmlWriteEvent}, +}; + +fn write_content( + writer: &mut XmlEventWriter, + content: &str, + tag: &str, +) -> Result<(), EncodeError> { + writer.write(XmlWriteEvent::start_element(tag))?; + if content.is_empty() { + // This doesn't feel like a great XML idiom + writer.write(XmlWriteEvent::start_element("null"))?; + } else { + writer.write(XmlWriteEvent::start_element("url"))?; + writer.write_string(content)?; + } + writer.write(XmlWriteEvent::end_element())?; + writer.write(XmlWriteEvent::end_element())?; + Ok(()) +} + +fn read_content_inner(reader: &mut XmlEventReader) -> Result { + match reader.expect_next()? { + XmlReadEvent::StartElement { + name, + attributes, + namespace, + } => match name.local_name.as_str() { + "null" => { + reader.expect_end_with_name("null")?; + Ok(String::new()) + } + "url" => { + let value = reader.read_characters()?; + reader.expect_end_with_name("url")?; + Ok(value) + } + _ => { + let event = XmlReadEvent::StartElement { + name, + attributes, + namespace, + }; + Err(reader.error(DecodeErrorKind::UnexpectedXmlEvent(event))) + } + }, + event => Err(reader.error(DecodeErrorKind::UnexpectedXmlEvent(event))), + } +} + +fn read_content(reader: &mut XmlEventReader, tag: &str) -> Result { + match reader.expect_next()? { + XmlReadEvent::StartElement { + name, + attributes, + namespace, + } => { + if name.local_name.as_str() != tag { + let event = XmlReadEvent::StartElement { + name, + attributes, + namespace, + }; + return Err(reader.error(DecodeErrorKind::UnexpectedXmlEvent(event))); + } + } + event => return Err(reader.error(DecodeErrorKind::UnexpectedXmlEvent(event))), + }; + + let value = read_content_inner(reader)?; + + reader.expect_end_with_name(tag)?; + + Ok(value) +} + +impl XmlType for Font { + const XML_TAG_NAME: &'static str = "Font"; + + fn write_xml(&self, writer: &mut XmlEventWriter) -> Result<(), EncodeError> { + write_content(writer, &self.family, "Family")?; + + writer.write_value_in_tag(&self.weight.as_u16(), "Weight")?; + + let style = match self.style { + FontStyle::Normal => "Normal", + FontStyle::Italic => "Italic", + }; + writer.write_tag_characters("Style", style)?; + + if let Some(ref cached_face_id) = self.cached_face_id { + write_content(writer, cached_face_id, "CachedFaceId")?; + } + + Ok(()) + } + + fn read_xml(reader: &mut XmlEventReader) -> Result { + let family = read_content(reader, "Family")?; + + let weight: u16 = reader.read_value_in_tag("Weight")?; + let weight = FontWeight::from_u16(weight); + + let style = match reader.read_tag_contents("Style")?.as_str() { + "Normal" => FontStyle::Normal, + "Italic" => FontStyle::Italic, + _ => FontStyle::Normal, + }; + + let cached_face_id = match reader.expect_peek()? { + XmlReadEvent::StartElement { name, .. } if name.local_name == "CachedFaceId" => { + Some(read_content(reader, "CachedFaceId")?) + } + _ => None, + }; + + Ok(Font { + family, + weight, + style, + cached_face_id, + }) + } +} + +#[cfg(test)] +mod test { + use super::*; + + use crate::test_util; + + #[test] + fn round_trip_font_face() { + test_util::test_xml_round_trip(&Font { + family: "rbxasset://fonts/families/SourceSansPro.json".to_owned(), + weight: FontWeight::Regular, + style: FontStyle::Normal, + cached_face_id: Some("rbxasset://fonts/SourceSansPro-Regular.ttf".to_owned()), + }); + } + + #[test] + fn deserialize_font_face() { + test_util::test_xml_deserialize( + r#" + + rbxasset://fonts/families/SourceSansPro.json + 400 + + rbxasset://fonts/SourceSansPro-Regular.ttf + + "#, + &Font { + family: "rbxasset://fonts/families/SourceSansPro.json".to_owned(), + weight: FontWeight::Regular, + style: FontStyle::Normal, + cached_face_id: Some("rbxasset://fonts/SourceSansPro-Regular.ttf".to_owned()), + }, + ); + } + + #[test] + fn serialize_font_face() { + test_util::test_xml_serialize( + r#" + + rbxasset://fonts/families/SourceSansPro.json + 400 + + rbxasset://fonts/SourceSansPro-Regular.ttf + + "#, + &Font { + family: "rbxasset://fonts/families/SourceSansPro.json".to_owned(), + weight: FontWeight::Regular, + style: FontStyle::Normal, + cached_face_id: Some("rbxasset://fonts/SourceSansPro-Regular.ttf".to_owned()), + }, + ); + } +} diff --git a/rbx_xml/src/types/mod.rs b/rbx_xml/src/types/mod.rs index f66a3f499..4856c7b55 100644 --- a/rbx_xml/src/types/mod.rs +++ b/rbx_xml/src/types/mod.rs @@ -17,6 +17,7 @@ mod colors; mod content; mod enumeration; mod faces; +mod font; mod number_range; mod number_sequence; mod numbers; @@ -34,7 +35,7 @@ mod vectors; use std::io::{Read, Write}; use rbx_dom_weak::types::{ - Axes, BinaryString, CFrame, Color3, Color3uint8, ColorSequence, Content, Enum, Faces, + Axes, BinaryString, CFrame, Color3, Color3uint8, ColorSequence, Content, Enum, Faces, Font, NumberRange, NumberSequence, PhysicalProperties, Ray, Rect, Ref, UDim, UDim2, Variant, Vector2, Vector2int16, Vector3, Vector3int16, }; @@ -133,6 +134,7 @@ declare_rbx_types! { Faces: Faces, Float32: f32, Float64: f64, + Font: Font, Int32: i32, Int64: i64, NumberRange: NumberRange, diff --git a/rbx_xml/src/types/numbers.rs b/rbx_xml/src/types/numbers.rs index 0d8846a62..2be02bc97 100644 --- a/rbx_xml/src/types/numbers.rs +++ b/rbx_xml/src/types/numbers.rs @@ -71,6 +71,7 @@ int_type!(i64, "int64"); // Convenience implementations for other types. // FIXME: This feels weird to bundle into the XmlType trait. int_type!(i16, ""); +int_type!(u16, ""); #[cfg(test)] mod test { diff --git a/test-files b/test-files index 1208331bf..1f1f25c70 160000 --- a/test-files +++ b/test-files @@ -1 +1 @@ -Subproject commit 1208331bf5c30b4071e8ab403349511de280df43 +Subproject commit 1f1f25c7098f257cd7fb7a19f0ad5308ea926b86