256 lines
7.9 KiB
C++
256 lines
7.9 KiB
C++
|
|
// 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;
|
||
|
|
|
||
|
|
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;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// 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 la topologie et AVANT de reconstruire,
|
||
|
|
// sans quoi la broche Font survit et l'erreur persiste.
|
||
|
|
FontPin->BreakAllPinLinks();
|
||
|
|
GetDefault<UEdGraphSchema_K2>()->ResetPinToAutogeneratedDefaultValue(FontPin, false);
|
||
|
|
|
||
|
|
NodesToReconstruct.AddUnique(GraphNode);
|
||
|
|
|
||
|
|
++NodesFixed;
|
||
|
|
++FixedHere;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// 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;
|
||
|
|
}
|