// Portage UE 5.5 - voir LPTestStringCompatCommandlet.h #include "LPTestStringCompatCommandlet.h" #include "Kismet/KismetStringLibrary.h" #include "LPStringCompat.h" #include DEFINE_LOG_CATEGORY_STATIC(LogLPTestStringCompat, Log, All); namespace { /** TCString::Stristr, UE 4.27 (CString.h l. 622-650), transcrit a l'identique. */ const TCHAR* Stristr427(const TCHAR* Str, const TCHAR* Find) { if (Find == nullptr || Str == nullptr) { return nullptr; } TCHAR FindInitial = FChar::ToUpper(*Find); int32 Length = FCString::Strlen(Find++) - 1; TCHAR StrChar = *Str++; while (StrChar) { StrChar = FChar::ToUpper(StrChar); if (StrChar == FindInitial && !FCString::Strnicmp(Str, Find, Length)) { return Str - 1; } StrChar = *Str++; } return nullptr; } /** FString::Find, UE 4.27 (String.cpp l. 180-227), transcrit a l'identique. */ int32 Find427(const FString& This, const TCHAR* SubStr, ESearchCase::Type SearchCase, ESearchDir::Type SearchDir, int32 StartPosition) { if (SubStr == nullptr) { return INDEX_NONE; } if (SearchDir == ESearchDir::FromStart) { const TCHAR* Start = *This; if (StartPosition != INDEX_NONE && This.Len() > 0) { Start += FMath::Clamp(StartPosition, 0, This.Len() - 1); } // FCString::Strstr 4.27 sous Windows : FWindowsPlatformString::Strstr = _tcsstr (wcsstr). const TCHAR* Tmp = SearchCase == ESearchCase::IgnoreCase ? Stristr427(Start, SubStr) : (const TCHAR*)wcsstr((const wchar_t*)Start, (const wchar_t*)SubStr); return Tmp ? UE_PTRDIFF_TO_INT32(Tmp - *This) : INDEX_NONE; } if (SearchCase == ESearchCase::IgnoreCase) { return Find427(This.ToUpper(), *FString(SubStr).ToUpper(), ESearchCase::CaseSensitive, SearchDir, StartPosition); } const int32 SearchStringLength = FMath::Max(1, FCString::Strlen(SubStr)); if (StartPosition == INDEX_NONE || StartPosition >= This.Len()) { StartPosition = This.Len(); } // En 4.27, (*this)[i+j] pouvait lire le '\0' final sans controle : on lit le tampon brut. const TCHAR* Data = *This; for (int32 i = StartPosition - SearchStringLength; i >= 0; i--) { int32 j; for (j = 0; SubStr[j]; j++) { if (Data[i + j] != SubStr[j]) { break; } } if (!SubStr[j]) { return i; } } return INDEX_NONE; } } ULPTestStringCompatCommandlet::ULPTestStringCompatCommandlet() { IsClient = false; IsServer = false; IsEditor = true; LogToConsole = true; } int32 ULPTestStringCompatCommandlet::Main(const FString& Params) { const TArray Strings = { TEXT(""), TEXT("a"), TEXT("A"), TEXT("abc"), TEXT("ABC"), TEXT("aXbXc"), TEXT("round, oval"), TEXT(" "), TEXT("teen"), TEXT("Daz") }; const TArray Subs = { TEXT(""), TEXT("a"), TEXT("A"), TEXT("b"), TEXT("X"), TEXT("bc"), TEXT("abc"), TEXT("abcd"), TEXT(", "), TEXT(" "), TEXT("oval"), TEXT("TEEN"), TEXT("daz") }; const TArray Starts = { -1, 0, 1, 2, 5, 50 }; int32 Cases = 0, CompatMismatch = 0, EngineMismatch = 0; for (const FString& S : Strings) { for (const FString& Sub : Subs) { for (int32 CaseIdx = 0; CaseIdx < 2; ++CaseIdx) { for (int32 DirIdx = 0; DirIdx < 2; ++DirIdx) { for (const int32 Start : Starts) { const ESearchCase::Type Case = CaseIdx ? ESearchCase::CaseSensitive : ESearchCase::IgnoreCase; const ESearchDir::Type Dir = DirIdx ? ESearchDir::FromEnd : ESearchDir::FromStart; ++Cases; const int32 Ref = Find427(S, *Sub, Case, Dir, Start); const int32 Compat = ULPStringCompat::FindUE4(S, Sub, Case, Dir, Start); const int32 Engine = S.Find(Sub, Case, Dir, Start); if (Compat != Ref) { ++CompatMismatch; UE_LOG(LogLPTestStringCompat, Error, TEXT("COMPAT Find(\"%s\", \"%s\", %s, %s, %d) : 4.27=%d compat=%d"), *S, *Sub, CaseIdx ? TEXT("Case") : TEXT("Ignore"), DirIdx ? TEXT("FromEnd") : TEXT("FromStart"), Start, Ref, Compat); } if (Engine != Ref) { ++EngineMismatch; UE_LOG(LogLPTestStringCompat, Display, TEXT("moteur Find(\"%s\", \"%s\", %s, %s, %d) : 4.27=%d 5.5=%d"), *S, *Sub, CaseIdx ? TEXT("Case") : TEXT("Ignore"), DirIdx ? TEXT("FromEnd") : TEXT("FromStart"), Start, Ref, Engine); } // Fonctions Blueprint : uniquement avec la position de depart par defaut. if (Start == -1) { const bool bUseCase = CaseIdx == 1, bFromEnd = DirIdx == 1; if (ULPStringCompat::Contains(S, Sub, bUseCase, bFromEnd) != (Ref != INDEX_NONE) || ULPStringCompat::FindSubstring(S, Sub, bUseCase, bFromEnd) != Ref) { ++CompatMismatch; UE_LOG(LogLPTestStringCompat, Error, TEXT("COMPAT Contains/FindSubstring(\"%s\", \"%s\") ecart"), *S, *Sub); } FString L1, R1, L2, R2; const bool B1 = ULPStringCompat::Split(S, Sub, L1, R1, Case, Dir); const bool B2 = Ref >= 0; if (B1 != B2 || (B2 && (L1 != S.Left(Ref) || R1 != S.Mid(Ref + Sub.Len())))) { ++CompatMismatch; UE_LOG(LogLPTestStringCompat, Error, TEXT("COMPAT Split(\"%s\", \"%s\") ecart"), *S, *Sub); } // Cas Blueprint par defaut, moteur 5.5 contre 4.27 : documente l'ecart vise. if (!bUseCase && !bFromEnd && UKismetStringLibrary::Contains(S, Sub) != (Ref != INDEX_NONE)) { UE_LOG(LogLPTestStringCompat, Display, TEXT("moteur Contains(\"%s\", \"%s\") : 4.27=%d 5.5=%d"), *S, *Sub, Ref != INDEX_NONE, UKismetStringLibrary::Contains(S, Sub)); } } } } } } } UE_LOG(LogLPTestStringCompat, Warning, TEXT("=== %d cas : compat != 4.27 : %d ; moteur 5.5 != 4.27 : %d ==="), Cases, CompatMismatch, EngineMismatch); return CompatMismatch > 0 ? 1 : 0; }