|
33 | 33 | #include <Unreal/Property/FNameProperty.hpp> |
34 | 34 | #include <Unreal/Property/FObjectProperty.hpp> |
35 | 35 | #include <Unreal/Property/FSetProperty.hpp> |
36 | | -#include <Unreal/Property/FStrProperty.hpp> |
| 36 | +#include <Unreal/CoreUObject/UObject/FStrProperty.hpp> |
| 37 | +#include <Unreal/CoreUObject/UObject/FUtf8StrProperty.hpp> |
| 38 | +#include <Unreal/CoreUObject/UObject/FAnsiStrProperty.hpp> |
37 | 39 | #include <Unreal/Property/FSoftClassProperty.hpp> |
38 | 40 | #include <Unreal/Property/FSoftObjectProperty.hpp> |
39 | 41 | #include <Unreal/Property/FStructProperty.hpp> |
@@ -1364,6 +1366,78 @@ namespace RC::UEGenerator |
1364 | 1366 | return; |
1365 | 1367 | } |
1366 | 1368 |
|
| 1369 | + // UTF8 String properties |
| 1370 | + if (property->IsA<FUtf8StrProperty>()) |
| 1371 | + { |
| 1372 | + FUtf8String* string_value = property->ContainerPtrToValuePtr<FUtf8String>(object); |
| 1373 | + // Convert UTF8 to native string type for comparison and generation |
| 1374 | + const char* utf8_cstr = reinterpret_cast<const char*>(**string_value); |
| 1375 | + const StringType string_value_string = to_generic_string(utf8_cstr); |
| 1376 | + |
| 1377 | + // Ensure property either does not exist in parent class or is overriden in the CDO for the child class |
| 1378 | + if (super_property != nullptr) |
| 1379 | + { |
| 1380 | + FUtf8String* super_string_value = super_property->ContainerPtrToValuePtr<FUtf8String>(super_object); |
| 1381 | + const char* super_utf8_cstr = reinterpret_cast<const char*>(**super_string_value); |
| 1382 | + const StringType super_string_value_string = to_generic_string(super_utf8_cstr); |
| 1383 | + if (string_value_string == super_string_value_string) |
| 1384 | + { |
| 1385 | + return; |
| 1386 | + } |
| 1387 | + super_and_no_access = private_access_modifier; |
| 1388 | + } |
| 1389 | + |
| 1390 | + if (string_value_string != STR("")) |
| 1391 | + { |
| 1392 | + // Create UTF8TEXT literal for UTF8 strings |
| 1393 | + const StringType result_value = create_utf8_string_literal(string_value_string); |
| 1394 | + if (!super_and_no_access) |
| 1395 | + { |
| 1396 | + generate_simple_assignment_expression(property, result_value, implementation_file, property_scope); |
| 1397 | + } |
| 1398 | + else |
| 1399 | + { |
| 1400 | + generate_advanced_assignment_expression(property, result_value, implementation_file, property_scope, property_type); |
| 1401 | + } |
| 1402 | + } |
| 1403 | + return; |
| 1404 | + } |
| 1405 | + |
| 1406 | + // ANSI String properties |
| 1407 | + if (property->IsA<FAnsiStrProperty>()) |
| 1408 | + { |
| 1409 | + FAnsiString* string_value = property->ContainerPtrToValuePtr<FAnsiString>(object); |
| 1410 | + // Convert ANSI to native string type for comparison and generation |
| 1411 | + const StringType string_value_string = to_generic_string(**string_value); |
| 1412 | + |
| 1413 | + // Ensure property either does not exist in parent class or is overriden in the CDO for the child class |
| 1414 | + if (super_property != nullptr) |
| 1415 | + { |
| 1416 | + FAnsiString* super_string_value = super_property->ContainerPtrToValuePtr<FAnsiString>(super_object); |
| 1417 | + const StringType super_string_value_string = to_generic_string(**super_string_value); |
| 1418 | + if (string_value_string == super_string_value_string) |
| 1419 | + { |
| 1420 | + return; |
| 1421 | + } |
| 1422 | + super_and_no_access = private_access_modifier; |
| 1423 | + } |
| 1424 | + |
| 1425 | + if (string_value_string != STR("")) |
| 1426 | + { |
| 1427 | + // ANSI strings use plain string literals (no TEXT macro) |
| 1428 | + const StringType result_value = create_ansi_string_literal(string_value_string); |
| 1429 | + if (!super_and_no_access) |
| 1430 | + { |
| 1431 | + generate_simple_assignment_expression(property, result_value, implementation_file, property_scope); |
| 1432 | + } |
| 1433 | + else |
| 1434 | + { |
| 1435 | + generate_advanced_assignment_expression(property, result_value, implementation_file, property_scope, property_type); |
| 1436 | + } |
| 1437 | + } |
| 1438 | + return; |
| 1439 | + } |
| 1440 | + |
1367 | 1441 | // Text properties are treated as FText::FromString, although it's not ideal |
1368 | 1442 | if (property->IsA<FTextProperty>()) |
1369 | 1443 | { |
@@ -1958,57 +2032,94 @@ namespace RC::UEGenerator |
1958 | 2032 | } |
1959 | 2033 |
|
1960 | 2034 | auto UEHeaderGenerator::create_string_literal(const StringType& string) -> StringType |
| 2035 | + { |
| 2036 | + return create_string_literal_with_macro(string, STR("TEXT")); |
| 2037 | + } |
| 2038 | + |
| 2039 | + auto UEHeaderGenerator::create_utf8_string_literal(const StringType& string) -> StringType |
| 2040 | + { |
| 2041 | + return create_string_literal_with_macro(string, STR("UTF8TEXT")); |
| 2042 | + } |
| 2043 | + |
| 2044 | + auto UEHeaderGenerator::create_ansi_string_literal(const StringType& string) -> StringType |
| 2045 | + { |
| 2046 | + // ANSI strings don't use a macro, just quotes |
| 2047 | + return create_string_literal_with_macro(string, STR("")); |
| 2048 | + } |
| 2049 | + |
| 2050 | + auto UEHeaderGenerator::create_string_literal_with_macro(const StringType& string, const StringType& macro_name) -> StringType |
1961 | 2051 | { |
1962 | 2052 | StringType result; |
1963 | | - result.append(STR("TEXT(\"")); |
| 2053 | + |
| 2054 | + // Open the literal |
| 2055 | + if (!macro_name.empty()) |
| 2056 | + { |
| 2057 | + result.append(macro_name); |
| 2058 | + result.append(STR("(\"")); |
| 2059 | + } |
| 2060 | + else |
| 2061 | + { |
| 2062 | + result.append(STR("\"")); |
| 2063 | + } |
1964 | 2064 |
|
1965 | 2065 | bool previous_character_was_hex = false; |
1966 | | - |
1967 | 2066 | const CharType* ptr = string.c_str(); |
| 2067 | + |
1968 | 2068 | while (CharType ch = *ptr++) |
1969 | 2069 | { |
1970 | 2070 | switch (ch) |
1971 | 2071 | { |
1972 | | - case STR('\r'): { |
| 2072 | + case STR('\r'): |
1973 | 2073 | continue; |
1974 | | - } |
1975 | | - case STR('\n'): { |
| 2074 | + case STR('\n'): |
1976 | 2075 | result.append(STR("\\n")); |
1977 | 2076 | previous_character_was_hex = false; |
1978 | 2077 | break; |
1979 | | - } |
1980 | | - case STR('\\'): { |
| 2078 | + case STR('\t'): |
| 2079 | + result.append(STR("\\t")); |
| 2080 | + previous_character_was_hex = false; |
| 2081 | + break; |
| 2082 | + case STR('\\'): |
1981 | 2083 | result.append(STR("\\\\")); |
1982 | 2084 | previous_character_was_hex = false; |
1983 | 2085 | break; |
1984 | | - } |
1985 | | - case STR('\"'): { |
| 2086 | + case STR('\"'): |
1986 | 2087 | result.append(STR("\\\"")); |
1987 | 2088 | previous_character_was_hex = false; |
1988 | 2089 | break; |
1989 | | - } |
1990 | | - default: { |
1991 | | - if (ch < 31 || ch >= 128) |
| 2090 | + default: |
| 2091 | + // Only escape control characters (< 32) |
| 2092 | + // For ANSI (no macro), also escape > 127 |
| 2093 | + if (ch < 32 || (macro_name.empty() && ch > 127)) |
1992 | 2094 | { |
1993 | | - result.append(fmt::format(STR("\\x{:04X}"), ch)); |
| 2095 | + result.append(fmt::format(STR("\\x{:02X}"), static_cast<unsigned char>(ch))); |
1994 | 2096 | previous_character_was_hex = true; |
1995 | 2097 | } |
1996 | 2098 | else |
1997 | 2099 | { |
1998 | | - // We close and open the literal (with TEXT) here in order to ensure that successive hex characters aren't |
1999 | | - // appended to the hex sequence, causing a different number |
| 2100 | + // Handle hex digit continuation |
2000 | 2101 | if (previous_character_was_hex && iswxdigit(ch) != 0) |
2001 | 2102 | { |
2002 | | - result.append(STR("\")TEXT(\"")); |
| 2103 | + if (!macro_name.empty()) |
| 2104 | + { |
| 2105 | + result.append(STR("\")")); |
| 2106 | + result.append(macro_name); |
| 2107 | + result.append(STR("(\"")); |
| 2108 | + } |
| 2109 | + else |
| 2110 | + { |
| 2111 | + result.append(STR("\"\"")); // String concatenation for ANSI |
| 2112 | + } |
2003 | 2113 | } |
2004 | 2114 | previous_character_was_hex = false; |
2005 | 2115 | result.push_back(ch); |
2006 | 2116 | } |
2007 | 2117 | break; |
2008 | 2118 | } |
2009 | | - } |
2010 | 2119 | } |
2011 | | - result.append(STR("\")")); |
| 2120 | + |
| 2121 | + // Close the literal |
| 2122 | + result.append(!macro_name.empty() ? STR("\")") : STR("\"")); |
2012 | 2123 | return result; |
2013 | 2124 | } |
2014 | 2125 |
|
@@ -2701,6 +2812,14 @@ namespace RC::UEGenerator |
2701 | 2812 | { |
2702 | 2813 | return STR("FString"); |
2703 | 2814 | } |
| 2815 | + else if (property->IsA<FUtf8StrProperty>()) |
| 2816 | + { |
| 2817 | + return STR("FUtf8String"); |
| 2818 | + } |
| 2819 | + else if (property->IsA<FAnsiStrProperty>()) |
| 2820 | + { |
| 2821 | + return STR("FAnsiString"); |
| 2822 | + } |
2704 | 2823 | else if (property->IsA<FTextProperty>()) |
2705 | 2824 | { |
2706 | 2825 | return STR("FText"); |
@@ -3221,8 +3340,12 @@ namespace RC::UEGenerator |
3221 | 3340 |
|
3222 | 3341 | // Force const reference when we're dealing with strings, and they are not passed by reference |
3223 | 3342 | // UHT for whatever reason completely strips const and reference flags from string properties, but happily generates them in boilerplate code |
| 3343 | + const StringType property_class_name = property->GetClass().GetName(); |
3224 | 3344 | const bool should_force_const_ref = |
3225 | | - ((property_flags & (CPF_ReferenceParm | CPF_OutParm)) == 0) && (property->GetClass().GetName() == STR("StrProperty")); |
| 3345 | + ((property_flags & (CPF_ReferenceParm | CPF_OutParm)) == 0) && |
| 3346 | + (property_class_name == STR("StrProperty") || |
| 3347 | + property_class_name == STR("Utf8StrProperty") || |
| 3348 | + property_class_name == STR("AnsiStrProperty")); |
3226 | 3349 |
|
3227 | 3350 | // Append const keyword to the parameter declaration if it is marked as const parameter |
3228 | 3351 | if ((property_flags & CPF_ConstParm) != 0 || should_force_const_ref) |
@@ -3417,6 +3540,14 @@ namespace RC::UEGenerator |
3417 | 3540 | { |
3418 | 3541 | return STR("TEXT(\"\")"); |
3419 | 3542 | } |
| 3543 | + if (field_class_name == STR("Utf8StrProperty")) |
| 3544 | + { |
| 3545 | + return STR("UTF8TEXT(\"\")"); |
| 3546 | + } |
| 3547 | + if (field_class_name == STR("AnsiStrProperty")) |
| 3548 | + { |
| 3549 | + return STR("\"\""); |
| 3550 | + } |
3420 | 3551 | if (field_class_name == STR("TextProperty")) |
3421 | 3552 | { |
3422 | 3553 | return STR("FText::GetEmpty()"); |
|
0 commit comments