You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#rb Zousar.Shaker #rnx #preflight 6270509a220f89f0ad573030 [CL 20016982 by Matt Peters in ue5-main branch]
349 lines
11 KiB
C++
349 lines
11 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "EditorScriptingHelpers.h"
|
|
#include "Utils.h"
|
|
#include "Algo/Count.h"
|
|
#include "AssetRegistry/AssetData.h"
|
|
#include "AssetRegistry/AssetRegistryModule.h"
|
|
#include "Misc/FileHelper.h"
|
|
#include "Misc/Paths.h"
|
|
#include "Editor.h"
|
|
|
|
namespace EditorScriptingHelpersInternal
|
|
{
|
|
// Like !FName::IsValidGroupName(Path)), but with another list and no conversion to from FName
|
|
// InvalidChar may be INVALID_OBJECTPATH_CHARACTERS or INVALID_LONGPACKAGE_CHARACTERS or ...
|
|
FString RemoveFullName(const FString& AnyAssetPath, FString& OutFailureReason)
|
|
{
|
|
FString Result = AnyAssetPath.TrimStartAndEnd();
|
|
int32 NumberOfSpace = Algo::Count(AnyAssetPath, TEXT(' '));
|
|
|
|
if (NumberOfSpace == 0)
|
|
{
|
|
return MoveTemp(Result);
|
|
}
|
|
else if (NumberOfSpace > 1)
|
|
{
|
|
OutFailureReason = FString::Printf(TEXT("Can't convert path '%s' because there are too many spaces."), *AnyAssetPath);
|
|
return FString();
|
|
}
|
|
else// if (NumberOfSpace == 1)
|
|
{
|
|
int32 FoundIndex = 0;
|
|
AnyAssetPath.FindChar(TEXT(' '), FoundIndex);
|
|
check(FoundIndex > INDEX_NONE && FoundIndex < AnyAssetPath.Len()); // because of TrimStartAndEnd
|
|
|
|
// Confirm that it's a valid Class
|
|
FString ClassName = AnyAssetPath.Left(FoundIndex);
|
|
|
|
// Convert \ to /
|
|
ClassName.ReplaceInline(TEXT("\\"), TEXT("/"), ESearchCase::CaseSensitive);
|
|
|
|
// Test ClassName for invalid Char
|
|
const int32 StrLen = FCString::Strlen(INVALID_OBJECTNAME_CHARACTERS);
|
|
for (int32 Index = 0; Index < StrLen; ++Index)
|
|
{
|
|
int32 InvalidFoundIndex = 0;
|
|
if (ClassName.FindChar(INVALID_OBJECTNAME_CHARACTERS[Index], InvalidFoundIndex))
|
|
{
|
|
OutFailureReason = FString::Printf(TEXT("Can't convert the path %s because it contains invalid characters (probably spaces)."), *AnyAssetPath);
|
|
return FString();
|
|
}
|
|
}
|
|
|
|
// Return the path without the Class name
|
|
return AnyAssetPath.Mid(FoundIndex + 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool EditorScriptingHelpers::CheckIfInEditorAndPIE()
|
|
{
|
|
if (!IsInGameThread())
|
|
{
|
|
UE_LOG(LogUtils, Error, TEXT("You are not on the main thread."));
|
|
return false;
|
|
}
|
|
if (!GIsEditor)
|
|
{
|
|
UE_LOG(LogUtils, Error, TEXT("You are not in the Editor."));
|
|
return false;
|
|
}
|
|
if (GEditor->PlayWorld || GIsPlayInEditorWorld)
|
|
{
|
|
UE_LOG(LogUtils, Error, TEXT("The Editor is currently in a play mode."));
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
FString EditorScriptingHelpers::ConvertAnyPathToLongPackagePath(const FString& AnyPath, FString& OutFailureReason)
|
|
{
|
|
if (AnyPath.Len() < 2) // minimal length to have /G
|
|
{
|
|
OutFailureReason = FString::Printf(TEXT("Can't convert the path '%s' because the Root path need to be specified. ie /Game/"), *AnyPath);
|
|
return FString();
|
|
}
|
|
|
|
// Prepare for TryConvertFilenameToLongPackageName
|
|
|
|
// Remove class name from Reference Path
|
|
FString TextPath = FPackageName::ExportTextPathToObjectPath(AnyPath);
|
|
|
|
// Remove class name Fullname
|
|
TextPath = EditorScriptingHelpersInternal::RemoveFullName(TextPath, OutFailureReason);
|
|
if (TextPath.IsEmpty())
|
|
{
|
|
return FString();
|
|
}
|
|
|
|
// Convert \ to /
|
|
TextPath.ReplaceInline(TEXT("\\"), TEXT("/"), ESearchCase::CaseSensitive);
|
|
FPaths::RemoveDuplicateSlashes(TextPath);
|
|
|
|
{
|
|
// Remove .
|
|
int32 ObjectDelimiterIdx;
|
|
if (TextPath.FindChar(TEXT('.'), ObjectDelimiterIdx))
|
|
{
|
|
TextPath.LeftInline(ObjectDelimiterIdx);
|
|
}
|
|
|
|
// Remove :
|
|
if (TextPath.FindChar(TEXT(':'), ObjectDelimiterIdx))
|
|
{
|
|
TextPath.LeftInline(ObjectDelimiterIdx);
|
|
}
|
|
}
|
|
|
|
// Test for invalid characters
|
|
if (!IsAValidPath(TextPath, INVALID_LONGPACKAGE_CHARACTERS, OutFailureReason))
|
|
{
|
|
return FString();
|
|
}
|
|
|
|
// Confirm that we have a valid Root Package and get the valid PackagePath /Game/MyFolder
|
|
FString PackagePath;
|
|
if (!FPackageName::TryConvertFilenameToLongPackageName(TextPath, PackagePath, &OutFailureReason))
|
|
{
|
|
return FString();
|
|
}
|
|
|
|
if (PackagePath.Len() == 0)
|
|
{
|
|
OutFailureReason = FString::Printf(TEXT("Can't convert the path '%s' because of an internal error. TryConvertFilenameToLongPackageName should have return false."), *AnyPath);
|
|
return FString();
|
|
}
|
|
|
|
if (PackagePath[0] != TEXT('/'))
|
|
{
|
|
OutFailureReason = FString::Printf(TEXT("Can't convert path '%s' because the PackagePath '%s' doesn't start with a '/'."), *AnyPath, *PackagePath);
|
|
return FString();
|
|
}
|
|
|
|
if (PackagePath[PackagePath.Len() - 1] == TEXT('/'))
|
|
{
|
|
PackagePath.RemoveAt(PackagePath.Len() - 1);
|
|
}
|
|
|
|
if (FPackageName::IsScriptPackage(PackagePath))
|
|
{
|
|
OutFailureReason = FString::Printf(TEXT("Can't convert the path '%s' because it starts with /Script/"), *AnyPath);
|
|
return FString();
|
|
}
|
|
if (FPackageName::IsMemoryPackage(PackagePath))
|
|
{
|
|
OutFailureReason = FString::Printf(TEXT("Can't convert the path '%s' because it starts with /Memory/"), *AnyPath);
|
|
return FString();
|
|
}
|
|
|
|
// Confirm that the PackagePath start with a valid root
|
|
if (!HasValidRoot(PackagePath))
|
|
{
|
|
OutFailureReason = FString::Printf(TEXT("Can't convert the path '%s' because it does not map to a root."), *AnyPath);
|
|
return FString();
|
|
}
|
|
|
|
return PackagePath;
|
|
}
|
|
|
|
bool EditorScriptingHelpers::HasValidRoot(const FString& ObjectPath)
|
|
{
|
|
FString Filename;
|
|
bool bValidRoot = true;
|
|
if (!ObjectPath.IsEmpty() && ObjectPath[ObjectPath.Len() - 1] == TEXT('/'))
|
|
{
|
|
bValidRoot = FPackageName::TryConvertLongPackageNameToFilename(ObjectPath, Filename);
|
|
}
|
|
else
|
|
{
|
|
FString ObjectPathWithSlash = ObjectPath;
|
|
ObjectPathWithSlash.AppendChar(TEXT('/'));
|
|
bValidRoot = FPackageName::TryConvertLongPackageNameToFilename(ObjectPathWithSlash, Filename);
|
|
}
|
|
|
|
return bValidRoot;
|
|
}
|
|
|
|
// Test for invalid characters
|
|
bool EditorScriptingHelpers::IsAValidPath(const FString& Path, const TCHAR* InvalidChar, FString& OutFailureReason)
|
|
{
|
|
const int32 StrLen = FCString::Strlen(InvalidChar);
|
|
for (int32 Index = 0; Index < StrLen; ++Index)
|
|
{
|
|
int32 FoundIndex = 0;
|
|
if (Path.FindChar(InvalidChar[Index], FoundIndex))
|
|
{
|
|
OutFailureReason = FString::Printf(TEXT("Can't convert the path %s because it contains invalid characters."), *Path);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (Path.Len() > FPlatformMisc::GetMaxPathLength())
|
|
{
|
|
OutFailureReason = FString::Printf(TEXT("Can't convert the path because it is too long (%d characters). This may interfere with cooking for consoles. Unreal filenames should be no longer than %d characters. Full path value: %s"), Path.Len(), FPlatformMisc::GetMaxPathLength(), *Path);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool EditorScriptingHelpers::IsAValidPathForCreateNewAsset(const FString& ObjectPath, FString& OutFailureReason)
|
|
{
|
|
const FString ObjectName = FPackageName::ObjectPathToObjectName(ObjectPath);
|
|
|
|
// Make sure the name is not already a class or otherwise invalid for saving
|
|
FText FailureReason;
|
|
if (!FFileHelper::IsFilenameValidForSaving(ObjectName, FailureReason))
|
|
{
|
|
OutFailureReason = FailureReason.ToString();
|
|
return false;
|
|
}
|
|
|
|
// Make sure the new name only contains valid characters
|
|
if (!FName::IsValidXName(ObjectName, INVALID_OBJECTNAME_CHARACTERS INVALID_LONGPACKAGE_CHARACTERS, &FailureReason))
|
|
{
|
|
OutFailureReason = FailureReason.ToString();
|
|
return false;
|
|
}
|
|
|
|
// Make sure we are not creating an FName that is too large
|
|
if (ObjectPath.Len() >= NAME_SIZE)
|
|
{
|
|
OutFailureReason = TEXT("This asset name is too long (") + FString::FromInt(ObjectPath.Len()) + TEXT(" characters), the maximum is ") + FString::FromInt(NAME_SIZE - 1) + TEXT(". Please choose a shorter name.");
|
|
return false;
|
|
}
|
|
|
|
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
|
|
FAssetData AssetData = AssetRegistryModule.Get().GetAssetByObjectPath(*ObjectPath);
|
|
if (AssetData.IsValid())
|
|
{
|
|
OutFailureReason = TEXT("An asset already exists at this location.");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
FString EditorScriptingHelpers::ConvertAnyPathToObjectPath(const FString& AnyAssetPath, FString& OutFailureReason)
|
|
{
|
|
if (AnyAssetPath.Len() < 2) // minimal length to have /G
|
|
{
|
|
OutFailureReason = FString::Printf(TEXT("Can't convert the path '%s' because the Root path need to be specified. ie /Game/"), *AnyAssetPath);
|
|
return FString();
|
|
}
|
|
|
|
// Remove class name from Reference Path
|
|
FString TextPath = FPackageName::ExportTextPathToObjectPath(AnyAssetPath);
|
|
|
|
// Remove class name Fullname
|
|
TextPath = EditorScriptingHelpersInternal::RemoveFullName(TextPath, OutFailureReason);
|
|
if (TextPath.IsEmpty())
|
|
{
|
|
return FString();
|
|
}
|
|
|
|
// Extract the subobject path if any
|
|
FString SubObjectPath;
|
|
int32 SubObjectDelimiterIdx;
|
|
if (TextPath.FindChar(SUBOBJECT_DELIMITER_CHAR, SubObjectDelimiterIdx))
|
|
{
|
|
SubObjectPath = TextPath.Mid(SubObjectDelimiterIdx + 1);
|
|
TextPath.LeftInline(SubObjectDelimiterIdx);
|
|
}
|
|
|
|
// Convert \ to /
|
|
TextPath.ReplaceInline(TEXT("\\"), TEXT("/"), ESearchCase::CaseSensitive);
|
|
FPaths::RemoveDuplicateSlashes(TextPath);
|
|
|
|
// Get asset full name, i.e."PackageName.ObjectName:InnerAssetName.2ndInnerAssetName" from "/Game/Folder/PackageName.ObjectName:InnerAssetName.2ndInnerAssetName"
|
|
FString AssetFullName;
|
|
{
|
|
// Get everything after the last slash
|
|
int32 IndexOfLastSlash = INDEX_NONE;
|
|
TextPath.FindLastChar('/', IndexOfLastSlash);
|
|
|
|
FString Folders = TextPath.Left(IndexOfLastSlash);
|
|
// Test for invalid characters
|
|
if (!IsAValidPath(Folders, INVALID_LONGPACKAGE_CHARACTERS, OutFailureReason))
|
|
{
|
|
return FString();
|
|
}
|
|
|
|
AssetFullName = TextPath.Mid(IndexOfLastSlash + 1);
|
|
}
|
|
|
|
// Get the object name
|
|
FString ObjectName = FPackageName::ObjectPathToObjectName(AssetFullName);
|
|
if (ObjectName.IsEmpty())
|
|
{
|
|
OutFailureReason = FString::Printf(TEXT("Can't convert the path '%s' because it doesn't contain an asset name."), *AnyAssetPath);
|
|
return FString();
|
|
}
|
|
|
|
// Test for invalid characters
|
|
if (!IsAValidPath(ObjectName, INVALID_OBJECTNAME_CHARACTERS, OutFailureReason))
|
|
{
|
|
return FString();
|
|
}
|
|
|
|
// Confirm that we have a valid Root Package and get the valid PackagePath /Game/MyFolder/MyAsset
|
|
FString PackagePath;
|
|
if (!FPackageName::TryConvertFilenameToLongPackageName(TextPath, PackagePath, &OutFailureReason))
|
|
{
|
|
return FString();
|
|
}
|
|
|
|
if (PackagePath.Len() == 0)
|
|
{
|
|
OutFailureReason = FString::Printf(TEXT("Can't convert path '%s' because the PackagePath is empty."), *AnyAssetPath);
|
|
return FString();
|
|
}
|
|
|
|
if (PackagePath[0] != TEXT('/'))
|
|
{
|
|
OutFailureReason = FString::Printf(TEXT("Can't convert path '%s' because the PackagePath '%s' doesn't start with a '/'."), *AnyAssetPath, *PackagePath);
|
|
return FString();
|
|
}
|
|
|
|
FString ObjectPath = FString::Printf(TEXT("%s.%s"), *PackagePath, *ObjectName);
|
|
|
|
if (FPackageName::IsScriptPackage(ObjectPath))
|
|
{
|
|
OutFailureReason = FString::Printf(TEXT("Can't convert the path '%s' because it start with /Script/"), *AnyAssetPath);
|
|
return FString();
|
|
}
|
|
if (FPackageName::IsMemoryPackage(ObjectPath))
|
|
{
|
|
OutFailureReason = FString::Printf(TEXT("Can't convert the path '%s' because it start with /Memory/"), *AnyAssetPath);
|
|
return FString();
|
|
}
|
|
|
|
// Confirm that the PackagePath starts with a valid root
|
|
if (!HasValidRoot(PackagePath))
|
|
{
|
|
OutFailureReason = FString::Printf(TEXT("Can't convert the path '%s' because it does not map to a root."), *AnyAssetPath);
|
|
return FString();
|
|
}
|
|
|
|
return ObjectPath;
|
|
} |