builds godot
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp
index 6a59942a56..773445edb6 100644
--- a/core/string/ustring.cpp
+++ b/core/string/ustring.cpp
@@ -1644,6 +1644,35 @@ String String::hex_encode_buffer(const uint8_t *p_buffer, int p_len) {
 	return ret;
 }
 
+Vector<uint8_t> String::hex_decode() const {
+	ERR_FAIL_COND_V_MSG(length() % 2 != 0, Vector<uint8_t>(), "Hexadecimal string of uneven length.");
+
+#define HEX_TO_BYTE(m_output, m_index)                                                                                   \
+	uint8_t m_output;                                                                                                    \
+	c = operator[](m_index);                                                                                             \
+	if (is_digit(c)) {                                                                                                   \
+		m_output = c - '0';                                                                                              \
+	} else if (c >= 'a' && c <= 'f') {                                                                                   \
+		m_output = c - 'a' + 10;                                                                                         \
+	} else if (c >= 'A' && c <= 'F') {                                                                                   \
+		m_output = c - 'A' + 10;                                                                                         \
+	} else {                                                                                                             \
+		ERR_FAIL_V_MSG(Vector<uint8_t>(), "Invalid hexadecimal character \"" + chr(c) + "\" at index " + m_index + "."); \
+	}
+
+	Vector<uint8_t> out;
+	int len = length() / 2;
+	out.resize(len);
+	for (int i = 0; i < len; i++) {
+		char32_t c;
+		HEX_TO_BYTE(first, i * 2);
+		HEX_TO_BYTE(second, i * 2 + 1);
+		out.write[i] = first * 16 + second;
+	}
+	return out;
+#undef HEX_TO_BYTE
+}
+
 void String::print_unicode_error(const String &p_message, bool p_critical) const {
 	if (p_critical) {
 		print_error(vformat("Unicode parsing error, some characters were replaced with spaces: %s", p_message));
diff --git a/core/string/ustring.h b/core/string/ustring.h
index 28e3af92c5..90034b1b07 100644
--- a/core/string/ustring.h
+++ b/core/string/ustring.h
@@ -321,6 +321,8 @@ public:
 	static String chr(char32_t p_char);
 	static String md5(const uint8_t *p_md5);
 	static String hex_encode_buffer(const uint8_t *p_buffer, int p_len);
+	Vector<uint8_t> hex_decode() const;
+
 	bool is_numeric() const;
 
 	double to_float() const;
diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp
index ae15158836..13e9da37f2 100644
--- a/core/variant/variant_call.cpp
+++ b/core/variant/variant_call.cpp
@@ -1734,6 +1734,7 @@ static void _register_variant_builtin_methods() {
 	bind_string_method(to_utf8_buffer, sarray(), varray());
 	bind_string_method(to_utf16_buffer, sarray(), varray());
 	bind_string_method(to_utf32_buffer, sarray(), varray());
+	bind_string_method(hex_decode, sarray(), varray());
 	bind_string_method(to_wchar_buffer, sarray(), varray());
 
 	bind_static_method(String, num_scientific, sarray("number"), varray());
diff --git a/doc/classes/String.xml b/doc/classes/String.xml
index 1f4ffa417d..854bb2b2f8 100644
--- a/doc/classes/String.xml
+++ b/doc/classes/String.xml
@@ -313,6 +313,24 @@
 				[b]Note:[/b] Strings with equal hash values are [i]not[/i] guaranteed to be the same, as a result of hash collisions. On the countrary, strings with different hash values are guaranteed to be different.
 			</description>
 		</method>
+		<method name="hex_decode" qualifiers="const">
+			<return type="PackedByteArray" />
+			<description>
+				Decodes a hexadecimal string as a [PackedByteArray].
+				[codeblocks]
+				[gdscript]
+				var text = "hello world"
+				var encoded = text.to_utf8_buffer().hex_encode()
+				print(buf.hex_decode().get_string_from_utf8())
+				[/gdscript]
+				[csharp]
+				var text = "hello world";
+				var encoded = text.ToUtf8Buffer().HexEncode();
+				GD.Print(buf.HexDecode().GetStringFromUtf8());
+				[/csharp]
+				[/codeblocks]
+			</description>
+		</method>
 		<method name="hex_to_int" qualifiers="const">
 			<return type="int" />
 			<description>
diff --git a/doc/classes/StringName.xml b/doc/classes/StringName.xml
index 2140c53e20..c41fcac033 100644
--- a/doc/classes/StringName.xml
+++ b/doc/classes/StringName.xml
@@ -296,6 +296,24 @@
 				[b]Note:[/b] Strings with equal hash values are [i]not[/i] guaranteed to be the same, as a result of hash collisions. On the countrary, strings with different hash values are guaranteed to be different.
 			</description>
 		</method>
+		<method name="hex_decode" qualifiers="const">
+			<return type="PackedByteArray" />
+			<description>
+				Decodes a hexadecimal string as a [PackedByteArray].
+				[codeblocks]
+				[gdscript]
+				var text = "hello world"
+				var encoded = text.to_utf8_buffer().hex_encode()
+				print(buf.hex_decode().get_string_from_utf8())
+				[/gdscript]
+				[csharp]
+				var text = "hello world";
+				var encoded = text.ToUtf8Buffer().HexEncode();
+				GD.Print(buf.HexDecode().GetStringFromUtf8());
+				[/csharp]
+				[/codeblocks]
+			</description>
+		</method>
 		<method name="hex_to_int" qualifiers="const">
 			<return type="int" />
 			<description>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
index df67e075ac..d53bb9f536 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/StringExtensions.cs
@@ -728,6 +728,26 @@ namespace Godot
             return hash;
         }
 
+        /// <summary>
+        /// Decodes a hexadecimal string.
+        /// </summary>
+        /// <param name="instance">The hexadecimal string.</param>
+        /// <returns>The byte array representation of this string.</returns>
+        public static byte[] HexDecode(this string instance)
+        {
+            if (instance.Length % 2 != 0)
+            {
+                throw new ArgumentException("Hexadecimal string of uneven length.", nameof(instance));
+            }
+            int len = instance.Length / 2;
+            byte[] ret = new byte[len];
+            for (int i = 0; i < len; i++)
+            {
+                ret[i] = (byte)int.Parse(instance.AsSpan(i * 2, 2), NumberStyles.AllowHexSpecifier);
+            }
+            return ret;
+        }
+
         /// <summary>
         /// Returns a hexadecimal representation of this byte as a string.
         /// </summary>