2026-09-28 22:56:10 +00:00
// Portage UE 5.5 - voir LPFixEditableTextBoxFontCommandlet.h
# include "LPFixEditableTextBoxFontCommandlet.h"
# include "AssetRegistry/AssetRegistryModule.h"
# include "EdGraph/EdGraph.h"
# include "EdGraph/EdGraphPin.h"
# include "EdGraphSchema_K2.h"
# include "Engine/Blueprint.h"
# include "K2Node_BreakStruct.h"
# include "K2Node_MakeStruct.h"
# include "Kismet2/BlueprintEditorUtils.h"
# include "Kismet2/KismetEditorUtilities.h"
# include "Misc/PackageName.h"
# include "Styling/SlateTypes.h"
# include "UObject/Package.h"
# include "UObject/SavePackage.h"
# include "UObject/UObjectGlobals.h"
DEFINE_LOG_CATEGORY_STATIC ( LogLPFixFont , Log , All ) ;
namespace
{
const TCHAR * OrphanPinName = TEXT ( " Font " ) ;
const TCHAR * TextStylePinName = TEXT ( " TextStyle " ) ;
/** Decrit une broche et ses liaisons, pour le rapport. */
FString DescribeLinks ( const UEdGraphPin * Pin )
{
if ( ! Pin )
{
return TEXT ( " (broche absente) " ) ;
}
if ( Pin - > LinkedTo . Num ( ) = = 0 )
{
return FString : : Printf ( TEXT ( " non connectee (defaut: '%s') " ) , * Pin - > DefaultValue ) ;
}
TArray < FString > Targets ;
for ( const UEdGraphPin * Linked : Pin - > LinkedTo )
{
Targets . Add ( FString : : Printf ( TEXT ( " %s.%s " ) ,
Linked - > GetOwningNode ( ) ? * Linked - > GetOwningNode ( ) - > GetName ( ) : TEXT ( " ? " ) ,
* Linked - > PinName . ToString ( ) ) ) ;
}
return FString : : Printf ( TEXT ( " %d liaison(s) -> %s " ) , Pin - > LinkedTo . Num ( ) , * FString : : Join ( Targets , TEXT ( " , " ) ) ) ;
}
}
ULPFixEditableTextBoxFontCommandlet : : ULPFixEditableTextBoxFontCommandlet ( )
{
IsClient = false ;
IsServer = false ;
IsEditor = true ;
LogToConsole = true ;
}
int32 ULPFixEditableTextBoxFontCommandlet : : Main ( const FString & Params )
{
const bool bDryRun = Params . Contains ( TEXT ( " DryRun " ) , ESearchCase : : IgnoreCase ) ;
UScriptStruct * EditableStruct = TBaseStructure < FEditableTextBoxStyle > : : Get ( ) ;
if ( ! EditableStruct )
{
UE_LOG ( LogLPFixFont , Error , TEXT ( " Structure FEditableTextBoxStyle introuvable. " ) ) ;
return 1 ;
}
UE_LOG ( LogLPFixFont , Warning , TEXT ( " === Rebranchement de la broche Font (EditableTextBoxStyle)%s === " ) ,
bDryRun ? TEXT ( " [DRY RUN] " ) : TEXT ( " " ) ) ;
FAssetRegistryModule & AssetRegistryModule =
FModuleManager : : LoadModuleChecked < FAssetRegistryModule > ( TEXT ( " AssetRegistry " ) ) ;
AssetRegistryModule . Get ( ) . SearchAllAssets ( true ) ;
TArray < FAssetData > BlueprintAssets ;
AssetRegistryModule . Get ( ) . GetAssetsByClass ( UBlueprint : : StaticClass ( ) - > GetClassPathName ( ) , BlueprintAssets , true ) ;
int32 NodesFound = 0 ;
int32 NodesFixed = 0 ;
int32 BlueprintsModified = 0 ;
int32 SaveFailures = 0 ;
int32 UnhandledLinks = 0 ;
for ( const FAssetData & AssetData : BlueprintAssets )
{
if ( ! AssetData . PackageName . ToString ( ) . StartsWith ( TEXT ( " /Game/ " ) ) )
{
continue ;
}
UBlueprint * Blueprint = Cast < UBlueprint > ( AssetData . GetAsset ( ) ) ;
if ( ! Blueprint )
{
continue ;
}
TArray < UEdGraph * > Graphs ;
Blueprint - > GetAllGraphs ( Graphs ) ;
int32 FixedHere = 0 ;
struct FPendingLink { UK2Node_BreakStruct * Break ; UK2Node_MakeStruct * Make ; } ;
TArray < FPendingLink > PendingLinks ;
TArray < UEdGraphNode * > NodesToReconstruct ;
2026-09-29 00:25:12 +00:00
TArray < UEdGraphPin * > FontPinsToClear ;
2026-09-28 22:56:10 +00:00
for ( UEdGraph * Graph : Graphs )
{
if ( ! Graph )
{
continue ;
}
TArray < UEdGraphNode * > Snapshot = Graph - > Nodes ;
for ( UEdGraphNode * GraphNode : Snapshot )
{
UK2Node_MakeStruct * MakeNode = Cast < UK2Node_MakeStruct > ( GraphNode ) ;
UK2Node_BreakStruct * BreakNode = Cast < UK2Node_BreakStruct > ( GraphNode ) ;
const bool bIsTarget =
( MakeNode & & MakeNode - > StructType = = EditableStruct ) | |
( BreakNode & & BreakNode - > StructType = = EditableStruct ) ;
if ( ! bIsTarget )
{
continue ;
}
UEdGraphPin * FontPin = GraphNode - > FindPin ( OrphanPinName ) ;
if ( ! FontPin )
{
continue ; // deja corrige
}
+ + NodesFound ;
UE_LOG ( LogLPFixFont , Warning , TEXT ( " %s :: %s -> %s " ) ,
* Blueprint - > GetName ( ) , * Graph - > GetName ( ) , * GraphNode - > GetName ( ) ) ;
UE_LOG ( LogLPFixFont , Warning , TEXT ( " broche Font : %s " ) , * DescribeLinks ( FontPin ) ) ;
UE_LOG ( LogLPFixFont , Warning , TEXT ( " broche TextStyle : %s " ) ,
* DescribeLinks ( GraphNode - > FindPin ( TextStylePinName ) ) ) ;
if ( bDryRun )
{
continue ;
}
// Topologie relevee sur CharGen et StatMenu :
// [Break EditableTextBoxStyle].Font -> [Make EditableTextBoxStyle].Font
// La police n'est pas construite, elle est recopiee d'un style vers un autre.
// Les deux extremites etant la meme structure et le champ ayant migre dans
// TextStyle, la correspondance fidele est TextStyle -> TextStyle : aucun
// noeud intermediaire n'est necessaire, et le reste du style suit.
if ( MakeNode )
{
for ( UEdGraphPin * Source : FontPin - > LinkedTo )
{
UK2Node_BreakStruct * SourceBreak = Cast < UK2Node_BreakStruct > ( Source - > GetOwningNode ( ) ) ;
if ( SourceBreak & & SourceBreak - > StructType = = EditableStruct )
{
PendingLinks . Add ( { SourceBreak , MakeNode } ) ;
}
else
{
UE_LOG ( LogLPFixFont , Error ,
TEXT ( " source Font inattendue (%s) : rebranchement a faire a la main. " ) ,
Source - > GetOwningNode ( ) ? * Source - > GetOwningNode ( ) - > GetName ( ) : TEXT ( " ? " ) ) ;
+ + UnhandledLinks ;
}
}
}
2026-09-29 00:25:12 +00:00
// Les liaisons ne sont coupees qu'APRES le releve complet (voir plus bas) :
// couper ici la broche Font d'un Break effacait aussi la liaison vue depuis
// les Make non encore parcourus, qui perdaient alors leur police sans
// rebranchement (CharGen, Make_64 - defaut releve le 2026-09-29, L4-CX-B02).
FontPinsToClear . AddUnique ( FontPin ) ;
2026-09-28 22:56:10 +00:00
NodesToReconstruct . AddUnique ( GraphNode ) ;
+ + NodesFixed ;
+ + FixedHere ;
}
}
2026-09-29 00:25:12 +00:00
// ReconstructNode conserve une broche orpheline dans DEUX cas : si elle
// porte des liaisons, et si elle porte une valeur differente du defaut.
// C'est le garde-fou d'UE contre la perte de donnees. Il faut donc lever
// les deux, APRES avoir releve toute la topologie et AVANT de reconstruire,
// sans quoi la broche Font survit et l'erreur persiste.
for ( UEdGraphPin * FontPin : FontPinsToClear )
{
FontPin - > BreakAllPinLinks ( ) ;
GetDefault < UEdGraphSchema_K2 > ( ) - > ResetPinToAutogeneratedDefaultValue ( FontPin , false ) ;
}
2026-09-28 22:56:10 +00:00
// La reconstruction invalide les broches : on la fait en bloc, puis on relie.
for ( UEdGraphNode * Node : NodesToReconstruct )
{
Node - > ReconstructNode ( ) ;
}
for ( const FPendingLink & Link : PendingLinks )
{
UEdGraphPin * From = Link . Break - > FindPin ( TextStylePinName , EGPD_Output ) ;
UEdGraphPin * To = Link . Make - > FindPin ( TextStylePinName , EGPD_Input ) ;
if ( From & & To )
{
// Idempotent : une seconde execution ne doit pas doubler la liaison.
if ( ! From - > LinkedTo . Contains ( To ) )
{
From - > MakeLinkTo ( To ) ;
}
UE_LOG ( LogLPFixFont , Warning , TEXT ( " %s.TextStyle -> %s.TextStyle " ) ,
* Link . Break - > GetName ( ) , * Link . Make - > GetName ( ) ) ;
}
else
{
+ + UnhandledLinks ;
UE_LOG ( LogLPFixFont , Error , TEXT ( " TextStyle introuvable sur %s ou %s. " ) ,
* Link . Break - > GetName ( ) , * Link . Make - > GetName ( ) ) ;
}
}
if ( FixedHere > 0 & & ! bDryRun )
{
+ + BlueprintsModified ;
FBlueprintEditorUtils : : MarkBlueprintAsStructurallyModified ( Blueprint ) ;
FKismetEditorUtilities : : CompileBlueprint ( Blueprint ) ;
UPackage * Package = Blueprint - > GetOutermost ( ) ;
FString Filename ;
if ( ! FPackageName : : TryConvertLongPackageNameToFilename (
Package - > GetName ( ) , Filename , FPackageName : : GetAssetPackageExtension ( ) ) )
{
+ + SaveFailures ;
UE_LOG ( LogLPFixFont , Error , TEXT ( " chemin introuvable pour %s " ) , * Package - > GetName ( ) ) ;
continue ;
}
ResetLoaders ( Package ) ;
FSavePackageArgs SaveArgs ;
SaveArgs . TopLevelFlags = RF_Public | RF_Standalone ;
SaveArgs . SaveFlags = SAVE_None ;
SaveArgs . bWarnOfLongFilename = true ;
// GWarn et non GError : en commandlet, GError rend toute erreur de save fatale.
SaveArgs . Error = GWarn ;
if ( UPackage : : SavePackage ( Package , nullptr , * Filename , SaveArgs ) )
{
UE_LOG ( LogLPFixFont , Warning , TEXT ( " -> %s sauvegarde (%d noeud(s)). " ) , * Package - > GetName ( ) , FixedHere ) ;
}
else
{
+ + SaveFailures ;
UE_LOG ( LogLPFixFont , Error , TEXT ( " -> ECHEC de sauvegarde de %s " ) , * Package - > GetName ( ) ) ;
}
}
}
UE_LOG ( LogLPFixFont , Warning , TEXT ( " === Termine : %d noeud(s) trouve(s), %d traite(s) dans %d Blueprint(s), %d liaison(s) non geree(s), %d echec(s) de sauvegarde. === " ) ,
NodesFound , NodesFixed , BlueprintsModified , UnhandledLinks , SaveFailures ) ;
return ( SaveFailures > 0 | | UnhandledLinks > 0 ) ? 1 : 0 ;
}