fixed build issues, added binaries, and updated the AsyncLoadingScreen plugin directory

This commit is contained in:
Devrim Yasar
2025-04-14 13:03:05 -05:00
parent b91d8c9412
commit cc50ba0c39
2200 changed files with 910483 additions and 318 deletions

View File

@ -0,0 +1,7 @@
{
"BuildId": "37670630",
"Modules":
{
"FileHelper": "UnrealEditor-FileHelper.dylib"
}
}

View File

@ -0,0 +1,7 @@
{
"BuildId": "37670630",
"Modules":
{
"FileHelper": "UnrealEditor-FileHelper-Win64-DebugGame.dll"
}
}

View File

@ -0,0 +1,7 @@
{
"BuildId": "37670630",
"Modules":
{
"FileHelper": "UnrealEditor-FileHelper.dll"
}
}

View File

@ -1,16 +1,16 @@
{
"FileVersion": 3,
"Version": 5,
"VersionName": "3.1",
"Version": 6,
"VersionName": "3.3",
"FriendlyName": "FileHelper",
"Description": "Helper functions to access and handle files on the file system",
"Category": "File",
"CreatedBy": "RLoris",
"CreatedByURL": "https://rloris.github.io",
"DocsURL": "https://github.com/RLoris/FileHelperDoc/",
"DocsURL": "https://github.com/RLoris/FileHelperDoc/blob/master/README.md",
"MarketplaceURL": "com.epicgames.launcher://ue/marketplace/product/bec5be3d587f4ff49a61d45d0e81e4c0",
"SupportURL": "https://forms.gle/CpekZfTewGZrUVen7",
"EngineVersion": "5.5.0+",
"EngineVersion": "5.5.0",
"CanContainContent": true,
"Installed": true,
"Modules": [

View File

@ -1,4 +1,4 @@
// Copyright 2023 RLoris
// Copyright 2025 RLoris
using UnrealBuildTool;
@ -7,44 +7,26 @@ public class FileHelper : ModuleRules
public FileHelper(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
PublicIncludePaths.AddRange(
new string[] {
}
);
PrivateIncludePaths.AddRange(
new string[] {
}
);
PrecompileForTargets = PrecompileTargetsType.Any;
PublicDependencyModuleNames.AddRange(
new string[]
{
"Core",
"Engine",
"JsonUtilities",
"Json"
}
);
);
PrivateDependencyModuleNames.AddRange(
new string[]
{
"CoreUObject",
"Engine",
"ImageWriteQueue",
"Slate",
"SlateCore"
}
);
DynamicallyLoadedModuleNames.AddRange(
new string[]
{
}
);
);
}
}

View File

@ -1,6 +1,7 @@
// Copyright 2023 RLoris
// Copyright 2025 RLoris
#include "FileHelperBPLibrary.h"
#include "HAL/PlatformFileManager.h"
#include "HAL/FileManager.h"
#include "Misc/FileHelper.h"
@ -9,22 +10,26 @@
#include "Misc/ConfigCacheIni.h"
#include "Engine/DataTable.h"
#include "Internationalization/Regex.h"
#include "Runtime/Launch/Resources/Version.h"
#include "Serialization/Csv/CsvParser.h"
#include "UObject/TextProperty.h"
class FCustomFileVisitor : public IPlatformFile::FDirectoryVisitor
{
public:
FCustomFileVisitor(const FString& Path, TArray<FString>& Paths, const FString& Pattern, bool File, bool Directory) : BasePath(Path), Nodes(Paths), Filter(Pattern), CustomPattern(Pattern), bFile(File), bDirectory(Directory) {};
//~ Begin FDirectoryVisitor
virtual bool Visit(const TCHAR* FilenameOrDirectory, bool bIsDirectory) override;
//~ End FDirectoryVisitor
private:
FString BasePath;
TArray<FString>& Nodes;
FString Filter;
FRegexPattern CustomPattern;
bool bFile = true;
bool bDirectory = true;
FCustomFileVisitor(FString& Path, TArray<FString>& Paths, const FString& Pattern, bool File, bool Directory) : BasePath(Path), Nodes(Paths), Filter(Pattern), CustomPattern(Pattern), bFile(File), bDirectory(Directory) {};
virtual bool Visit(const TCHAR* FilenameOrDirectory, bool bIsDirectory);
};
FEnginePath UFileHelperBPLibrary::GetEngineDirectories()
@ -38,7 +43,11 @@ FEnginePath UFileHelperBPLibrary::GetEngineDirectories()
P.Saved =FPaths::ConvertRelativePathToFull( FPaths::EngineSavedDir());
P.User = FPaths::ConvertRelativePathToFull(FPaths::EngineUserDir());
P.DefaultLayout = FPaths::ConvertRelativePathToFull(FPaths::EngineDefaultLayoutDir());
#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 4
P.PlatformExtensions = FPaths::ConvertRelativePathToFull(FPaths::EnginePlatformExtensionDir(TEXT("")).TrimChar('/'));
#else
P.PlatformExtensions = FPaths::ConvertRelativePathToFull(FPaths::EnginePlatformExtensionsDir());
#endif
P.UserLayout = FPaths::ConvertRelativePathToFull(FPaths::EngineUserLayoutDir());
return P;
}
@ -56,14 +65,18 @@ FProjectPath UFileHelperBPLibrary::GetProjectDirectories()
P.Saved = FPaths::ConvertRelativePathToFull(FPaths::ProjectSavedDir());
P.User = FPaths::ConvertRelativePathToFull(FPaths::ProjectUserDir());
P.PersistentDownload = FPaths::ConvertRelativePathToFull(FPaths::ProjectPersistentDownloadDir());
#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 4
P.PlatformExtensions = FPaths::ConvertRelativePathToFull(FPaths::ProjectPlatformExtensionDir(TEXT("")).TrimChar('/'));
#else
P.PlatformExtensions = FPaths::ConvertRelativePathToFull(FPaths::ProjectPlatformExtensionsDir());
#endif
return P;
}
bool UFileHelperBPLibrary::ReadText(FString Path, FString& Output)
{
IPlatformFile& FileManager = FPlatformFileManager::Get().GetPlatformFile();
if (FileManager.FileExists(*Path))
if (FileManager.FileExists(*Path))
{
return FFileHelper::LoadFileToString(Output, *Path);
}
@ -369,10 +382,10 @@ bool FCustomFileVisitor::Visit(const TCHAR* FilenameOrDirectory, bool bIsDirecto
{
FString RelativePath = FString(FilenameOrDirectory);
FPaths::MakePathRelativeTo(RelativePath, *BasePath);
if (!Filter.IsEmpty())
if (!Filter.IsEmpty())
{
FRegexMatcher CustomMatcher(CustomPattern, RelativePath);
if (CustomMatcher.FindNext())
if (CustomMatcher.FindNext())
{
Nodes.Add(RelativePath);
}
@ -388,21 +401,21 @@ bool FCustomFileVisitor::Visit(const TCHAR* FilenameOrDirectory, bool bIsDirecto
bool UFileHelperBPLibrary::ListDirectory(FString Path, FString Pattern, TArray<FString>& Nodes, bool ShowFile, bool ShowDirectory, bool Recursive)
{
IPlatformFile& FileManager = FPlatformFileManager::Get().GetPlatformFile();
if (!FileManager.DirectoryExists(*Path))
if (!FileManager.DirectoryExists(*Path))
{
return false;
}
if (!ShowDirectory && !ShowFile)
{
return true;
}
}
FString BasePath = FPaths::Combine(Path, TEXT("/"));
FCustomFileVisitor CustomFileVisitor(BasePath, Nodes, Pattern, ShowFile, ShowDirectory);
if (Recursive)
{
return FileManager.IterateDirectoryRecursively(*Path, CustomFileVisitor);
}
else
else
{
return FileManager.IterateDirectory(*Path, CustomFileVisitor);
}
@ -614,14 +627,14 @@ bool UFileHelperBPLibrary::DataTableToJSON(UDataTable* Table, FString& Output)
UDataTable* UFileHelperBPLibrary::CSVToDataTable(FString CSV, UScriptStruct* Struct, bool& Success)
{
Success = false;
if (Struct == nullptr)
if (Struct == nullptr)
{
return nullptr;
}
UDataTable* DataTable = NewObject<UDataTable>();
DataTable->RowStruct = Struct;
auto Result = DataTable->CreateTableFromCSVString(CSV);
if (Result.Num() == 0)
if (Result.Num() == 0)
{
Success = true;
}
@ -645,7 +658,7 @@ UDataTable* UFileHelperBPLibrary::JSONToDataTable(FString JSON, UScriptStruct* S
return DataTable;
}
void UFileHelperBPLibrary::ReadConfig(FString FilePath, FString Section, FString Key, bool& Success, bool SingleLineArrayRead, UStruct*& OutValue, bool bLoadFromDisk)
void UFileHelperBPLibrary::ReadConfig(FString FilePath, FString Section, FString Key, bool& Success, bool SingleLineArrayRead, UStruct*& OutValue)
{
checkNoEntry();
}
@ -673,13 +686,17 @@ TArray<FString> UFileHelperBPLibrary::SplitString(FString String, FString Separa
return Array;
}
bool UFileHelperBPLibrary::WriteConfigFile(FString Filename, FString Section, FString Key, FProperty* Type, void* Value, bool SingleLineArray, bool bWriteToDisk)
bool UFileHelperBPLibrary::WriteConfigFile(FString Filename, FString Section, FString Key, FProperty* Type, void* Value, bool SingleLineArray)
{
if (!GConfig)
{
return false;
}
if (FBoolProperty* BoolProperty = CastField<FBoolProperty>(Type))
FConfigFile ConfigFile;
FindOrCreateConfigFile(Filename, ConfigFile);
if (FBoolProperty* BoolProperty = CastField<FBoolProperty>(Type))
{
GConfig->SetBool(*Section, *Key, *(static_cast<bool*>(Value)), Filename);
}
@ -768,33 +785,19 @@ bool UFileHelperBPLibrary::WriteConfigFile(FString Filename, FString Section, FS
return false;
}
if (bWriteToDisk)
{
GConfig->Flush(false, Filename);
}
return true;
return SaveConfigFile(Filename);
}
bool UFileHelperBPLibrary::ReadConfigFile(FString Filename, FString Section, FString Key, FProperty* Type, void* Value, bool SingleLineArray, bool bLoadFromDisk)
bool UFileHelperBPLibrary::ReadConfigFile(FString Filename, FString Section, FString Key, FProperty* Type, void* Value, bool SingleLineArray)
{
if (!GConfig)
{
return false;
}
if (bLoadFromDisk)
{
FConfigFile* ConfigFile = GConfig->FindConfigFile(Filename);
if (!ConfigFile)
{
return false;
}
ConfigFile->Read(Filename);
}
FConfigFile ConfigFile;
FindOrCreateConfigFile(Filename, ConfigFile);
bool Success = false;
if (FBoolProperty* BoolProperty = CastField<FBoolProperty>(Type))
{
@ -868,32 +871,83 @@ bool UFileHelperBPLibrary::ReadConfigFile(FString Filename, FString Section, FSt
}
}
}
return Success;
}
void UFileHelperBPLibrary::WriteConfig(FString FilePath, FString Section, FString Key, bool& Success, bool SingleLineArrayWrite, const UStruct* Value, bool bWriteToDisk)
void UFileHelperBPLibrary::FindOrCreateConfigFile(const FString& InFilePath, FConfigFile& OutConfigFile)
{
const bool bDisabled = GConfig->AreFileOperationsDisabled();
if (bDisabled)
{
GConfig->EnableFileOperations();
}
FConfigFile* ConfigFile = GConfig->Find(InFilePath);
if (!ConfigFile)
{
FConfigFile NewConfigFile;
NewConfigFile.Read(InFilePath);
OutConfigFile = GConfig->Add(InFilePath, NewConfigFile);
}
else
{
OutConfigFile = *ConfigFile;
}
if (bDisabled)
{
GConfig->DisableFileOperations();
}
}
bool UFileHelperBPLibrary::SaveConfigFile(const FString& InFilePath)
{
if (FConfigFile* ConfigFile = GConfig->Find(InFilePath))
{
const bool bDisabled = GConfig->AreFileOperationsDisabled();
if (bDisabled)
{
GConfig->EnableFileOperations();
}
GConfig->Flush(false, InFilePath);
if (bDisabled)
{
GConfig->DisableFileOperations();
}
return true;
}
return false;
}
void UFileHelperBPLibrary::WriteConfig(FString FilePath, FString Section, FString Key, bool& Success, bool SingleLineArrayWrite, const UStruct* Value)
{
checkNoEntry();
}
bool UFileHelperBPLibrary::RemoveConfig(FString FilePath, FString Section, FString Key, bool bWriteToDisk)
bool UFileHelperBPLibrary::RemoveConfig(FString FilePath, FString Section, FString Key)
{
if (!GConfig)
{
return false;
}
FConfigFile ConfigFile;
FindOrCreateConfigFile(FilePath, ConfigFile);
if (!GConfig->RemoveKey(*Section, *Key, *FilePath))
{
return false;
}
if (bWriteToDisk)
{
GConfig->Flush(false, FilePath);
}
return true;
return SaveConfigFile(FilePath);
}
// equivalent GetTableAsCSV()

View File

@ -1,4 +1,4 @@
// Copyright 2023 RLoris
// Copyright 2025 RLoris
#include "Modules/ModuleManager.h"

View File

@ -1,25 +1,33 @@
// Copyright 2023 RLoris
// Copyright 2025 RLoris
#include "FileHelperScreenshotAction.h"
#include "Components/SceneCaptureComponent2D.h"
#include "Engine/TextureRenderTarget2D.h"
#include "ImageUtils.h"
#include "Kismet/GameplayStatics.h"
#include "TextureResource.h"
#include "UnrealClient.h"
#include "Async/Async.h"
#include "Camera/CameraActor.h"
#include "Camera/CameraComponent.h"
#include "Components/SceneCaptureComponent2D.h"
#include "Engine/TextureRenderTarget2D.h"
#include "Engine/Engine.h"
#include "Framework/Application/SlateApplication.h"
#include "HAL/PlatformFileManager.h"
#include "IImageWrapper.h"
#include "ImageUtils.h"
#include "ImageWriteQueue.h"
#include "ImageWriteTask.h"
#include "Kismet/GameplayStatics.h"
#include "Misc/Paths.h"
#include "Misc/FileHelper.h"
#include "Modules/ModuleManager.h"
#include "TextureResource.h"
#include "UnrealClient.h"
#include "Widgets/SViewport.h"
UFileHelperScreenshotAction* UFileHelperScreenshotAction::TakeScreenshot(UObject* InWorldContextObject, const FFileHelperScreenshotActionOptions& InOptions)
UFileHelperScreenshotAction* UFileHelperScreenshotAction::TakeScreenshot(const FFileHelperScreenshotActionOptions& InOptions)
{
UFileHelperScreenshotAction* Node = NewObject<UFileHelperScreenshotAction>();
Node->Options = InOptions;
Node->Options.Filename = FPaths::GetBaseFilename(Node->Options.Filename);
Node->Options.Filename = FPaths::GetBaseFilename(Node->Options.Filename, /** Remove path */true);
Node->bActive = false;
Node->WorldContextObject = InWorldContextObject;
// not garbage collected
Node->AddToRoot();
return Node;
}
@ -37,13 +45,8 @@ void UFileHelperScreenshotAction::Activate()
return;
}
if (!WorldContextObject || !WorldContextObject->GetWorld() || !WorldContextObject->GetWorld()->GetGameViewport())
{
FFrame::KismetExecutionMessage(TEXT("Invalid WorldContextObject. Cannot execute ScreenshotUtility"), ELogVerbosity::Error);
OnTaskFailed();
return;
}
Reset();
FText ErrorFilename;
if (!FFileHelper::IsFilenameValidForSaving(Options.Filename, ErrorFilename))
{
@ -51,44 +54,38 @@ void UFileHelperScreenshotAction::Activate()
OnTaskFailed();
return;
}
const FString FinalFilename = (Options.bPrefixTimestamp ? (FDateTime::Now().ToString(TEXT("%Y_%m_%d__%H_%M_%S__"))) : "") + Options.Filename;
if (!FScreenshotRequest::IsScreenshotRequested())
{
bActive = true;
ScreenshotTexture = nullptr;
if (!Options.CustomCameraActor)
{
constexpr bool bAddFilenameSuffix = false;
const FViewport* Viewport = WorldContextObject->GetWorld()->GetGameViewport()->Viewport;
const bool bHDREnabled = Viewport->GetSceneHDREnabled();
FScreenshotRequest::Reset();
FScreenshotRequest::RequestScreenshot(FinalFilename, Options.bShowUI, bAddFilenameSuffix, bHDREnabled && Options.bWithHDR);
FilePath = FScreenshotRequest::GetFilename();
FScreenshotRequest::OnScreenshotRequestProcessed().RemoveAll(this);
FScreenshotRequest::OnScreenshotRequestProcessed().AddUObject(this, &UFileHelperScreenshotAction::OnTaskCompleted);
bActive = true;
ScreenshotTexture = nullptr;
const FString FinalFilename = (Options.bPrefixTimestamp ? (FDateTime::Now().ToString(TEXT("%Y_%m_%d__%H_%M_%S__"))) : "") + Options.Filename;
FilePath = FPaths::Combine(Options.DirectoryPath, FinalFilename);
if (!Options.CustomCameraActor)
{
if (Options.bShowUI)
{
CreateViewportScreenshot();
}
else
{
FilePath = FPaths::ScreenShotDir() + FinalFilename + TEXT(".png");
CreateCustomCameraScreenshot();
CreatePlayerPOVScreenshot();
}
}
else
{
CreateCustomCameraScreenshot();
}
}
void UFileHelperScreenshotAction::OnTaskCompleted()
{
IPlatformFile& FileManager = FPlatformFileManager::Get().GetPlatformFile();
if (!ScreenshotTexture && FileManager.FileExists(*FilePath))
if (!ScreenshotTexture && FileManager.FileExists(*FilePath))
{
ScreenshotTexture = FImageUtils::ImportFileAsTexture2D(FilePath);
}
if (ScreenshotTexture)
{
Completed.Broadcast(ScreenshotTexture, FilePath);
@ -97,7 +94,7 @@ void UFileHelperScreenshotAction::OnTaskCompleted()
{
OnTaskFailed();
}
Reset();
}
@ -109,15 +106,15 @@ void UFileHelperScreenshotAction::OnTaskFailed()
void UFileHelperScreenshotAction::CreateCustomCameraScreenshot()
{
UWorld* World = WorldContextObject->GetWorld();
if (!World || !World->GetGameViewport() || !World->GetGameViewport()->Viewport)
const ACameraActor* Camera = Options.CustomCameraActor;
if (!Camera)
{
OnTaskFailed();
return;
}
const ACameraActor* Camera = Options.CustomCameraActor;
if (!Camera)
UWorld* World = Camera->GetWorld();
if (!World)
{
OnTaskFailed();
return;
@ -129,74 +126,316 @@ void UFileHelperScreenshotAction::CreateCustomCameraScreenshot()
OnTaskFailed();
return;
}
/*const APlayerCameraManager* PlayerCamera = UGameplayStatics::GetPlayerCameraManager(WorldContextObject, 0);
if (!PlayerCamera)
{
OnTaskFailed();
return;
}*/
const FViewport* GameViewport = World->GetGameViewport()->Viewport;
const FIntRect ViewRect(0, 0, GameViewport->GetSizeXY().X, GameViewport->GetSizeXY().Y);
const FVector CameraLocation = Camera->GetActorLocation();
const FRotator CameraRotation = Camera->GetActorRotation();
const float CameraFOV = CameraComponent->FieldOfView;
const ECameraProjectionMode::Type CameraProjectionMode = CameraComponent->ProjectionMode;
const float CameraOrthoWidth = CameraComponent->OrthoWidth;
CreateRenderTargetScreenshot(GWorld, CameraFOV, CameraProjectionMode, CameraOrthoWidth, CameraLocation, CameraRotation, ViewRect.Width(), ViewRect.Height());
}
void UFileHelperScreenshotAction::CreateViewportScreenshot()
{
if (!GEngine || !GEngine->GameViewport || !GEngine->GameViewport->Viewport)
{
OnTaskFailed();
return;
}
FViewport* Viewport = GEngine->GameViewport->Viewport;
// Get viewport size
const FIntPoint ViewportSize = Viewport->GetSizeXY();
const int32 Width = ViewportSize.X;
const int32 Height = ViewportSize.Y;
TWeakObjectPtr<UFileHelperScreenshotAction> ThisWeak(this);
auto ThenTask = [ThisWeak](const TFuture<bool>& InFuture)
{
bool bResult = InFuture.Get();
AsyncTask(ENamedThreads::Type::GameThread, [ThisWeak, bResult]()
{
UFileHelperScreenshotAction* This = ThisWeak.Get();
if (!This)
{
return;
}
if (!bResult)
{
This->OnTaskFailed();
return;
}
UTexture2D* Texture = FImageUtils::ImportFileAsTexture2D(This->FilePath);
if (!Texture)
{
This->OnTaskFailed();
return;
}
This->ScreenshotTexture = Texture;
This->OnTaskCompleted();
});
};
if (Options.bWithHDR && Viewport->IsHDRViewport())
{
TArray<FLinearColor> OutPixels;
OutPixels.Reserve(Width * Height);
if (!Viewport->ReadLinearColorPixels(OutPixels, FReadSurfaceDataFlags(), FIntRect(0, 0, Width, Height)) || OutPixels.IsEmpty())
{
OnTaskFailed();
return;
}
OutPixels.Shrink();
// If UI is enabled, capture it separately and blend it
const TSharedPtr<SViewport> ViewportWidget = GEngine->GameViewport->GetGameViewportWidget();
if (Options.bShowUI && ViewportWidget.IsValid())
{
FIntVector OutSize;
if (!FSlateApplication::Get().TakeHDRScreenshot(ViewportWidget.ToSharedRef(), OutPixels, OutSize))
{
OnTaskFailed();
return;
}
}
FilePath += TEXT(".exr");
WriteLinearColorBufferToDiskAsync(MoveTemp(OutPixels), Width, Height)
.Then(ThenTask);
}
else
{
TArray<FColor> OutPixels;
OutPixels.Reserve(Width * Height);
if (!Viewport->ReadPixels(OutPixels, FReadSurfaceDataFlags(), FIntRect(0, 0, Width, Height)) || OutPixels.IsEmpty())
{
OnTaskFailed();
return;
}
OutPixels.Shrink();
// If UI is enabled, capture it separately and blend it
const TSharedPtr<SViewport> ViewportWidget = GEngine->GameViewport->GetGameViewportWidget();
if (Options.bShowUI && ViewportWidget.IsValid())
{
FIntVector OutSize;
if (!FSlateApplication::Get().TakeScreenshot(ViewportWidget.ToSharedRef(), OutPixels, OutSize))
{
OnTaskFailed();
return;
}
}
for (FColor& Color : OutPixels)
{
Color.A = 255;
}
FilePath += TEXT(".png");
WriteColorBufferToDiskAsync(MoveTemp(OutPixels), Width, Height)
.Then(ThenTask);
}
}
void UFileHelperScreenshotAction::CreatePlayerPOVScreenshot()
{
if (!GEngine || !GEngine->GameViewport || !GEngine->GameViewport->Viewport)
{
OnTaskFailed();
return;
}
const APlayerController* PlayerController = UGameplayStatics::GetPlayerController(GWorld, 0);
if (!PlayerController || !PlayerController->PlayerCameraManager)
{
OnTaskFailed();
return;
}
FVector2D ViewportSize;
GEngine->GameViewport->GetViewportSize(ViewportSize);
FVector CameraLocation;
FRotator CameraRotation;
PlayerController->GetPlayerViewPoint(CameraLocation, CameraRotation);
const float FOV = PlayerController->PlayerCameraManager->GetFOVAngle();
constexpr ECameraProjectionMode::Type ProjectionMode = ECameraProjectionMode::Perspective;
CreateRenderTargetScreenshot(GWorld, FOV, ProjectionMode, 0.f, CameraLocation, CameraRotation, ViewportSize.X, ViewportSize.Y);
}
void UFileHelperScreenshotAction::CreateRenderTargetScreenshot(UWorld* InWorld, float InFOV, ECameraProjectionMode::Type InProjectionMode, float InOrthoWidth, const FVector& InLocation, const FRotator& InRotation, int32 InWidth, int32 InHeight)
{
USceneCaptureComponent2D* SceneComponent = NewObject<USceneCaptureComponent2D>(this, TEXT("SceneComponent"));
SceneComponent->RegisterComponentWithWorld(World);
SceneComponent->RegisterComponentWithWorld(InWorld);
SceneComponent->bCaptureEveryFrame = false;
SceneComponent->bCaptureOnMovement = false;
SceneComponent->bAlwaysPersistRenderingState = true;
SceneComponent->CaptureSource = ESceneCaptureSource::SCS_FinalColorHDR;
SceneComponent->FOVAngle = CameraComponent->FieldOfView;
SceneComponent->ProjectionType = CameraComponent->ProjectionMode;
SceneComponent->OrthoWidth = CameraComponent->OrthoWidth;
SceneComponent->SetWorldLocationAndRotation(CameraLocation, CameraRotation);
SceneComponent->FOVAngle = InFOV;
SceneComponent->ProjectionType = InProjectionMode;
SceneComponent->OrthoWidth = InOrthoWidth;
SceneComponent->SetWorldLocationAndRotation(InLocation, InRotation);
UTextureRenderTarget2D* TextureRenderTarget = NewObject<UTextureRenderTarget2D>();
TextureRenderTarget->InitCustomFormat(ViewRect.Width(),ViewRect.Height(),PF_B8G8R8A8,false);
TextureRenderTarget->InitCustomFormat(InWidth,InHeight,PF_B8G8R8A8,false);
TextureRenderTarget->UpdateResourceImmediate();
SceneComponent->TextureTarget = TextureRenderTarget;
SceneComponent->CaptureScene();
TArray<FColor> OutColors;
OutColors.Reserve(ViewRect.Width() * ViewRect.Height());
TextureRenderTarget->GameThread_GetRenderTargetResource()->ReadPixels(OutColors);
OutColors.Shrink();
SceneComponent->UnregisterComponent();
if (OutColors.Num() == 0)
SceneComponent->MarkAsGarbage();
FTextureRenderTargetResource* RenderTargetResource = TextureRenderTarget->GameThread_GetRenderTargetResource();
if (!RenderTargetResource)
{
OnTaskFailed();
return;
}
TWeakObjectPtr<UFileHelperScreenshotAction> ThisWeak(this);
auto ThenTask = [ThisWeak](const TFuture<bool>& InFuture)
{
bool bResult = InFuture.Get();
AsyncTask(ENamedThreads::Type::GameThread, [ThisWeak, bResult]()
{
UFileHelperScreenshotAction* This = ThisWeak.Get();
if (!This)
{
return;
}
if (!bResult)
{
This->OnTaskFailed();
return;
}
UTexture2D* Texture = FImageUtils::ImportFileAsTexture2D(This->FilePath);
if (!Texture)
{
This->OnTaskFailed();
return;
}
This->ScreenshotTexture = Texture;
This->OnTaskCompleted();
});
};
if (Options.bWithHDR && RenderTargetResource->GetSceneHDREnabled())
{
TArray<FLinearColor> OutPixels;
OutPixels.Reserve(InWidth * InHeight);
if (!RenderTargetResource->ReadLinearColorPixels(OutPixels) || OutPixels.IsEmpty())
{
OnTaskFailed();
return;
}
OutPixels.Shrink();
FilePath += TEXT(".exr");
WriteLinearColorBufferToDiskAsync(MoveTemp(OutPixels), InWidth, InHeight)
.Then(ThenTask);
}
else
{
TArray<FColor> OutPixels;
OutPixels.Reserve(InWidth * InHeight);
if (!RenderTargetResource->ReadPixels(OutPixels) || OutPixels.IsEmpty())
{
OnTaskFailed();
return;
}
OutPixels.Shrink();
for (FColor& Color : OutPixels)
{
Color.A = 255;
}
FilePath += TEXT(".png");
WriteColorBufferToDiskAsync(MoveTemp(OutPixels), InWidth, InHeight)
.Then(ThenTask);
}
TextureRenderTarget->MarkAsGarbage();
}
bool UFileHelperScreenshotAction::WriteColorBufferToDisk(const TArray<FColor>& InBuffer, int32 InWidth, int32 InHeight, UTexture2D*& OutTexture) const
{
if (InBuffer.IsEmpty())
{
return false;
}
TArray<uint8> OutImage;
FImageUtils::ThumbnailCompressImageArray(ViewRect.Width(), ViewRect.Height(), OutColors, OutImage);
if (OutImage.Num() == 0)
FImageUtils::ThumbnailCompressImageArray(InWidth, InHeight, InBuffer, OutImage);
if (OutImage.IsEmpty())
{
OnTaskFailed();
return;
return false;
}
if (!FFileHelper::SaveArrayToFile(OutImage, *FilePath))
{
OnTaskFailed();
return;
return false;
}
OutTexture = FImageUtils::ImportBufferAsTexture2D(OutImage);
return IsValid(OutTexture);
}
TFuture<bool> UFileHelperScreenshotAction::WriteLinearColorBufferToDiskAsync(TArray<FLinearColor>&& InBuffer, int32 InWidth, int32 InHeight)
{
IImageWriteQueueModule& ImageWriteQueueModule = FModuleManager::LoadModuleChecked<IImageWriteQueueModule>("ImageWriteQueue");
TUniquePtr<FImageWriteTask> ImageTask = MakeUnique<FImageWriteTask>();
ImageTask->Format = EImageFormat::EXR;
ImageTask->PixelData = MakeUnique<TImagePixelData<FLinearColor>>(FIntPoint(InWidth, InHeight), TArray64<FLinearColor>(InBuffer));
ImageTask->Filename = FilePath;
ImageTask->bOverwriteFile = true;
ImageTask->CompressionQuality = 100;
return ImageWriteQueueModule.GetWriteQueue().Enqueue(MoveTemp(ImageTask));
}
TFuture<bool> UFileHelperScreenshotAction::WriteColorBufferToDiskAsync(TArray<FColor>&& InBuffer, int32 InWidth, int32 InHeight)
{
IImageWriteQueueModule& ImageWriteQueueModule = FModuleManager::LoadModuleChecked<IImageWriteQueueModule>("ImageWriteQueue");
TUniquePtr<FImageWriteTask> ImageTask = MakeUnique<FImageWriteTask>();
ImageTask->Format = EImageFormat::PNG;
ImageTask->PixelData = MakeUnique<TImagePixelData<FColor>>(FIntPoint(InWidth, InHeight), TArray64<FColor>(InBuffer));
ImageTask->Filename = FilePath;
ImageTask->bOverwriteFile = true;
ImageTask->CompressionQuality = 100;
return ImageWriteQueueModule.GetWriteQueue().Enqueue(MoveTemp(ImageTask));
}
void UFileHelperScreenshotAction::ConvertLinearColorToColorBuffer(const TArray<FLinearColor>& InSourceBuffer, TArray<FColor>& OutDestBuffer)
{
OutDestBuffer.Empty(InSourceBuffer.Num());
for (const FLinearColor& LinearColor : InSourceBuffer)
{
OutDestBuffer.Emplace(LinearColor.ToFColor(false)); // Apply gamma correction
}
ScreenshotTexture = FImageUtils::ImportBufferAsTexture2D(OutImage);
OnTaskCompleted();
}
void UFileHelperScreenshotAction::Reset()
{
WorldContextObject = nullptr;
ScreenshotTexture = nullptr;
bActive = false;
FilePath.Empty();
FScreenshotRequest::Reset();
FScreenshotRequest::OnScreenshotRequestProcessed().RemoveAll(this);
RemoveFromRoot();
}

View File

@ -1,4 +1,4 @@
// Copyright 2023 RLoris
// Copyright 2025 RLoris
#pragma once
@ -6,6 +6,7 @@
#include "Kismet/BlueprintFunctionLibrary.h"
#include "FileHelperBPLibrary.generated.h"
class FConfigFile;
class UDataTable;
USTRUCT(BlueprintType)
@ -13,25 +14,24 @@ struct FCustomNodeStat
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintReadOnly, Category = "FileHelper|FileSystem")
bool IsDirectory;
UPROPERTY(BlueprintReadOnly, Category = "FileHelper|FileSystem")
bool IsReadOnly;
UPROPERTY(BlueprintReadOnly, Category = "FileHelper|FileSystem")
FDateTime LastAccessTime;
UPROPERTY(BlueprintReadOnly, Category = "FileHelper|FileSystem")
FDateTime CreationTime;
UPROPERTY(BlueprintReadOnly, Category = "FileHelper|FileSystem")
FDateTime ModificationTime;
UPROPERTY(BlueprintReadOnly, Category = "FileHelper|FileSystem")
int64 FileSize;
FCustomNodeStat()
: IsDirectory(false)
, IsReadOnly(false)
@ -46,38 +46,37 @@ USTRUCT(BlueprintType)
struct FProjectPath
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintReadOnly, Category = "FileHelper|Path")
FString Directory;
UPROPERTY(BlueprintReadOnly, Category = "FileHelper|Path")
FString Config;
UPROPERTY(BlueprintReadOnly, Category = "FileHelper|Path")
FString Content;
UPROPERTY(BlueprintReadOnly, Category = "FileHelper|Path")
FString Intermediate;
UPROPERTY(BlueprintReadOnly, Category = "FileHelper|Path")
FString Log;
UPROPERTY(BlueprintReadOnly, Category = "FileHelper|Path")
FString Mods;
UPROPERTY(BlueprintReadOnly, Category = "FileHelper|Path")
FString Plugins;
UPROPERTY(BlueprintReadOnly, Category = "FileHelper|Path")
FString Saved;
UPROPERTY(BlueprintReadOnly, Category = "FileHelper|Path")
FString User;
UPROPERTY(BlueprintReadOnly, Category = "FileHelper|Path")
FString PersistentDownload;
UPROPERTY(BlueprintReadOnly, Category = "FileHelper|Path")
FString PlatformExtensions;
};
@ -86,35 +85,34 @@ USTRUCT(BlueprintType)
struct FEnginePath
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintReadOnly, Category = "FileHelper|Path")
FString Directory;
UPROPERTY(BlueprintReadOnly, Category = "FileHelper|Path")
FString Config;
UPROPERTY(BlueprintReadOnly, Category = "FileHelper|Path")
FString Content;
UPROPERTY(BlueprintReadOnly, Category = "FileHelper|Path")
FString Intermediate;
UPROPERTY(BlueprintReadOnly, Category = "FileHelper|Path")
FString Plugins;
UPROPERTY(BlueprintReadOnly, Category = "FileHelper|Path")
FString Saved;
UPROPERTY(BlueprintReadOnly, Category = "FileHelper|Path")
FString User;
UPROPERTY(BlueprintReadOnly, Category = "FileHelper|Path")
FString DefaultLayout;
UPROPERTY(BlueprintReadOnly, Category = "FileHelper|Path")
FString PlatformExtensions;
UPROPERTY(BlueprintReadOnly, Category = "FileHelper|Path")
FString UserLayout;
};
@ -123,132 +121,131 @@ UCLASS()
class UFileHelperBPLibrary : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
/* Paths */
/* Path */
UFUNCTION(BlueprintPure, meta = (DisplayName = "GetEngineDirectories", CompactNodeTitle = "EngineDirs", Keywords = "File plugin path engine directory", ToolTip = "Gets the engine directories"), Category = "FileHelper|Path")
static FEnginePath GetEngineDirectories();
UFUNCTION(BlueprintPure, meta = (DisplayName = "GetProjectDirectories", CompactNodeTitle = "ProjectDirs", Keywords = "File plugin path project directory", ToolTip = "Gets the project directories"), Category = "FileHelper|Path")
static FProjectPath GetProjectDirectories();
/* Text file */
/* File */
UFUNCTION(BlueprintCallable, meta = (DisplayName = "ReadTextFile", CompactNodeTitle = "ReadText", Keywords = "File plugin read text", ToolTip = "Read a standard text file"), Category = "FileHelper|File|Text")
static bool ReadText(FString Path, FString& Output);
UFUNCTION(BlueprintCallable, meta = (DisplayName = "WriteTextFile", CompactNodeTitle = "WriteText", Keywords = "File plugin write text", ToolTip = "Save a standard text file"), Category = "FileHelper|File|Text")
static bool SaveText(FString Path, FString Text, FString& Error, bool Append = false, bool Force = false);
UFUNCTION(BlueprintCallable, meta = (DisplayName = "ReadLineFile", CompactNodeTitle = "ReadLine", Keywords = "File plugin read text lines pattern", ToolTip = "Read the lines of a standard text file"), Category = "FileHelper|File|Text")
static bool ReadLine(FString Path, FString Pattern, TArray<FString>& Lines);
UFUNCTION(BlueprintCallable, meta = (DisplayName = "ReadLineRangeFile", CompactNodeTitle = "ReadLineRange", Keywords = "File plugin read text lines range", ToolTip = "Read range of lines of a standard text file"), Category = "FileHelper|File|Text")
static bool ReadLineRange(FString InPath, TArray<FString>& OutLines, int32 InStartIdx = 0, int32 InEndIdx = -1);
UFUNCTION(BlueprintCallable, meta = (DisplayName = "WriteLineFile", CompactNodeTitle = "WriteLine", Keywords = "File plugin write text lines", ToolTip = "Save lines in a standard text file"), Category = "FileHelper|File|Text")
static bool SaveLine(FString Path, const TArray<FString>& Text, FString& Error, bool Append = false, bool Force = false);
UFUNCTION(BlueprintCallable, meta = (DisplayName = "ReadByteFile", CompactNodeTitle = "ReadByte", Keywords = "File plugin read byte", ToolTip = "Read byte file"), Category = "FileHelper|File|Byte")
static bool ReadByte(FString Path, TArray<uint8>& Bytes);
UFUNCTION(BlueprintCallable, meta = (DisplayName = "WriteByteFile", CompactNodeTitle = "WriteByte", Keywords = "File plugin write byte", ToolTip = "Save byte to file"), Category = "FileHelper|File|Byte")
static bool SaveByte(FString Path, const TArray<uint8>& Bytes, FString& Error, bool Append = false, bool Force = false);
/* Base64 */
UFUNCTION(BlueprintPure, meta = (DisplayName = "BytesToBase64", CompactNodeTitle = "ToBase64", Keywords = "File plugin bytes convert base64 encode", ToolTip = "Encodes a byte array to base64"), Category = "FileHelper|File|Byte")
static FString BytesToBase64(const TArray<uint8> Bytes);
UFUNCTION(BlueprintPure, meta = (DisplayName = "BytesFromBase64", CompactNodeTitle = "FromBase64", Keywords = "File plugin bytes convert base64 decode", ToolTip = "Decodes a byte array from base64"), Category = "FileHelper|File|Byte")
static bool BytesFromBase64(const FString Source, TArray<uint8>& Out);
/* CSV file */
UFUNCTION(BlueprintCallable, meta = (DisplayName = "WriteCSVFile", CompactNodeTitle = "WriteCSV", Keywords = "File plugin write csv", ToolTip = "Save a csv file"), Category = "FileHelper|File|CSV")
static bool SaveCSV(FString Path, TArray<FString> Headers, TArray<FString> Data, int32& Total, bool Force = false);
UFUNCTION(BlueprintCallable, meta = (DisplayName = "ReadCSVFile", CompactNodeTitle = "ReadCSV", Keywords = "File plugin read csv", ToolTip = "Read a csv file"), Category = "FileHelper|File|CSV")
static bool ReadCSV(FString Path, TArray<FString>& Headers, TArray<FString>& Data, int32& Total, bool HeaderFirst = true);
/* Network */
UFUNCTION(BlueprintPure, meta = (DisplayName = "BytesToBase64", CompactNodeTitle = "ToBase64", Keywords = "File plugin bytes convert base64 encode", ToolTip = "Encodes a byte array to base64"), Category = "FileHelper|File|Byte")
static FString BytesToBase64(const TArray<uint8> Bytes);
UFUNCTION(BlueprintPure, meta = (DisplayName = "BytesFromBase64", CompactNodeTitle = "FromBase64", Keywords = "File plugin bytes convert base64 decode", ToolTip = "Decodes a byte array from base64"), Category = "FileHelper|File|Byte")
static bool BytesFromBase64(const FString Source, TArray<uint8>& Out);
/* CSV convert */
UFUNCTION(BlueprintCallable, meta = (DisplayName = "StringToCSV", CompactNodeTitle = "StrToCSV", Keywords = "File plugin string csv", ToolTip = "convert a string to csv"), Category = "FileHelper|CSV")
static bool StringToCSV(FString Content, TArray<FString>& Headers, TArray<FString>& Data, int32& Total, bool HeaderFirst = true);
UFUNCTION(BlueprintCallable, meta = (DisplayName = "CSVToString", CompactNodeTitle = "CSVToStr", Keywords = "File plugin csv string", ToolTip = "convert a csv to string"), Category = "FileHelper|CSV")
static bool CSVToString(FString& Result, TArray<FString> Headers, TArray<FString> Data, int32& Total);
/* File system */
UFUNCTION(BlueprintCallable, meta = (DisplayName = "IsFile", CompactNodeTitle = "IsFile", Keywords = "File plugin check file exist", ToolTip = "Check whether a file exists"), Category = "FileHelper|FileSystem")
static bool IsFile(FString Path);
UFUNCTION(BlueprintCallable, meta = (DisplayName = "IsDirectory", CompactNodeTitle = "IsDir", Keywords = "File plugin check directory exist", ToolTip = "Check whether a directory exists"), Category = "FileHelper|FileSystem")
static bool IsDirectory(FString Path);
UFUNCTION(BlueprintPure, meta = (DisplayName = "IsValidFilename", CompactNodeTitle = "IsValidName", Keywords = "File plugin check filename valid", ToolTip = "Check whether a filename is valid and can be used"), Category = "FileHelper|FileSystem")
static bool IsValidFilename(FString Filename);
UFUNCTION(BlueprintPure, meta = (DisplayName = "IsValidPath", CompactNodeTitle = "IsValidPath", Keywords = "File plugin check path valid", ToolTip = "Check whether a path is valid and can be used"), Category = "FileHelper|FileSystem")
static bool IsValidPath(FString Path);
UFUNCTION(BlueprintPure, meta = (DisplayName = "ValidateFilename", CompactNodeTitle = "ValidateName", Keywords = "File plugin validate path", ToolTip = "Validate a filename to be used on this file system"), Category = "FileHelper|FileSystem")
static bool ValidateFilename(FString Filename, FString& ValidName);
UFUNCTION(BlueprintCallable, meta = (DisplayName = "SetReadOnlyFlag", CompactNodeTitle = "SetReadOnly", Keywords = "File plugin read only path", ToolTip = "Updates the read only property on file"), Category = "FileHelper|FileSystem")
static bool SetReadOnlyFlag(FString FilePath, bool Flag);
UFUNCTION(BlueprintCallable, meta = (DisplayName = "GetReadOnlyFlag", CompactNodeTitle = "IsReadOnly", Keywords = "File plugin read only path", ToolTip = "Gets the read only property on file"), Category = "FileHelper|FileSystem")
static bool GetReadOnlyFlag(FString FilePath);
UFUNCTION(BlueprintCallable, meta = (DisplayName = "GetFileSize", CompactNodeTitle = "GetSize", Keywords = "File plugin size directory", ToolTip = "Gets the size of a file"), Category = "FileHelper|FileSystem")
static int64 GetFileSize(FString FilePath);
UFUNCTION(BlueprintCallable, meta = (DisplayName = "ListDirectory", CompactNodeTitle = "LsDir", Keywords = "File plugin list directory pattern regex recursive", ToolTip = "List nodes from directory"), Category = "FileHelper|FileSystem")
static bool ListDirectory(FString Path, FString Pattern, TArray<FString>& Nodes, bool ShowFile = true, bool ShowDirectory = true, bool Recursive = false);
UFUNCTION(BlueprintCallable, meta = (DisplayName = "MakeDirectory", CompactNodeTitle = "MkDir", Keywords = "File plugin make directory recursive", ToolTip = "Create a new directory"), Category = "FileHelper|FileSystem")
static bool MakeDirectory(FString Path, bool Recursive = true);
UFUNCTION(BlueprintCallable, meta = (DisplayName = "RemoveDirectory", CompactNodeTitle = "RmDir", Keywords = "File plugin remove directory recursive", ToolTip = "Removes a directory"), Category = "FileHelper|FileSystem")
static bool RemoveDirectory(FString Path, bool Recursive = false);
UFUNCTION(BlueprintCallable, meta = (DisplayName = "CopyDirectory", CompactNodeTitle = "CpDir", Keywords = "File plugin copy directory recursive", ToolTip = "Copies a directory"), Category = "FileHelper|FileSystem")
static bool CopyDirectory(FString Source, FString Dest);
UFUNCTION(BlueprintCallable, meta = (DisplayName = "MoveDirectory", CompactNodeTitle = "MvDir", Keywords = "File plugin move directory recursive", ToolTip = "Moves a directory"), Category = "FileHelper|FileSystem")
static bool MoveDirectory(FString Source, FString Dest);
UFUNCTION(BlueprintCallable, meta = (DisplayName = "NodeStats", CompactNodeTitle = "NodeStats", Keywords = "File plugin stats directory node", ToolTip = "Gets the stats of a node"), Category = "FileHelper|FileSystem")
static bool NodeStats(FString Path, FCustomNodeStat& Stats);
UFUNCTION(BlueprintCallable, meta = (DisplayName = "RemoveFile", CompactNodeTitle = "RmFile", Keywords = "File plugin remove file recursive", ToolTip = "Removes a file"), Category = "FileHelper|FileSystem")
static bool RemoveFile(FString Path);
UFUNCTION(BlueprintCallable, meta = (DisplayName = "CopyFile", CompactNodeTitle = "CpFile", Keywords = "File plugin copy file recursive", ToolTip = "Copies a file"), Category = "FileHelper|FileSystem")
static bool CopyFile(FString Source, FString Dest, bool Force = false);
UFUNCTION(BlueprintCallable, meta = (DisplayName = "MoveFile", CompactNodeTitle = "MvFile", Keywords = "File plugin move file recursive", ToolTip = "Moves a file"), Category = "FileHelper|FileSystem")
static bool MoveFile(FString Source, FString Dest, bool Force = false);
UFUNCTION(BlueprintCallable, meta = (DisplayName = "RenameFile", CompactNodeTitle = "RenameFile", Keywords = "File plugin rename file recursive", ToolTip = "Renames a file"), Category = "FileHelper|FileSystem")
static bool RenameFile(FString Path, FString NewName);
UFUNCTION(BlueprintPure, meta = (DisplayName = "PathParts", Keywords = "File plugin path parts", ToolTip = "Gets the parts of a path"), Category = "FileHelper|FileSystem")
static void GetPathParts(FString Path, FString& PathPart, FString& BasePart, FString& ExtensionPart, FString& FileName);
/* Datatable */
UFUNCTION(BlueprintCallable, meta = (DisplayName = "DataTableToCSV", Keywords = "File plugin datatable csv convert export", ToolTip = "Converts a datatable to csv string"), Category = "FileHelper|Datatable")
static bool DatatableToCSV(UDataTable* Table, FString& Output);
UFUNCTION(BlueprintCallable, meta = (DisplayName = "DataTableToJSON", Keywords = "File plugin datatable json convert export", ToolTip = "Converts a datatable to json string"), Category = "FileHelper|Datatable")
static bool DataTableToJSON(UDataTable* Table, FString& Output);
UFUNCTION(BlueprintCallable, meta = (DisplayName = "CSVToDataTable", Keywords = "File plugin datatable csv convert import", ToolTip = "Converts a csv string to datatable"), Category = "FileHelper|Datatable")
static UDataTable* CSVToDataTable(FString CSV, UScriptStruct* Struct, bool& Success);
UFUNCTION(BlueprintCallable, meta = (DisplayName = "JSONToDataTable", Keywords = "File plugin datatable json convert import", ToolTip = "Converts a json string to datatable"), Category = "FileHelper|Datatable")
static UDataTable* JSONToDataTable(FString JSON, UScriptStruct* Struct, bool& Success);
/* Config file ini */
/** Reads a value at a specific key in a section from a config file */
UFUNCTION(BlueprintCallable, Category = "FileHelper|Config", CustomThunk, meta = (CustomStructureParam = "OutValue"))
static void ReadConfig(FString FilePath, FString Section, FString Key, bool& Success, bool SingleLineArrayRead, UStruct*& OutValue, bool bLoadFromDisk = false);
static void ReadConfig(FString FilePath, FString Section, FString Key, bool& Success, bool SingleLineArrayRead, UStruct*& OutValue);
DECLARE_FUNCTION(execReadConfig)
{
P_GET_PROPERTY(FStrProperty, FilePath);
@ -262,15 +259,14 @@ public:
FProperty* Property = Stack.MostRecentProperty;
void* ValuePtr = Stack.MostRecentPropertyAddress;
P_GET_UBOOL(bLoadFromDisk);
P_FINISH;
Success = UFileHelperBPLibrary::ReadConfigFile(FilePath, Section, Key, Property, ValuePtr, SingleLineArrayRead, bLoadFromDisk);
Success = UFileHelperBPLibrary::ReadConfigFile(FilePath, Section, Key, Property, ValuePtr, SingleLineArrayRead);
}
/** Writes a value at a specific key in a section from a config file, also creates the file when not found */
UFUNCTION(BlueprintCallable, Category = "FileHelper|Config", CustomThunk, meta = (CustomStructureParam = "Value"))
static void WriteConfig(FString FilePath, FString Section, FString Key, bool& Success, bool SingleLineArrayWrite, const UStruct* Value, bool bWriteToDisk = true);
static void WriteConfig(FString FilePath, FString Section, FString Key, bool& Success, bool SingleLineArrayWrite, const UStruct* Value);
DECLARE_FUNCTION(execWriteConfig)
{
P_GET_PROPERTY(FStrProperty, FilePath);
@ -284,23 +280,24 @@ public:
FProperty* Property = Stack.MostRecentProperty;
void* ValuePtr = Stack.MostRecentPropertyAddress;
P_GET_UBOOL(bWriteToDisk);
P_FINISH;
Success = UFileHelperBPLibrary::WriteConfigFile(FilePath, Section, Key, Property, ValuePtr, SingleLineArrayWrite, bWriteToDisk);
Success = UFileHelperBPLibrary::WriteConfigFile(FilePath, Section, Key, Property, ValuePtr, SingleLineArrayWrite);
}
/** Removes a specific key in a section from a config file, also deletes the file when empty */
UFUNCTION(BlueprintCallable, Category = "FileHelper|Config")
static bool RemoveConfig(FString FilePath, FString Section, FString Key, bool bWriteToDisk);
static bool RemoveConfig(FString FilePath, FString Section, FString Key);
protected:
/* Utility */
static TArray<FString> SplitString(FString String, FString Separator, ESearchCase::Type SearchCase);
static bool StringArrayToCSV(TArray<FString> Lines, TArray<FString>& Headers, TArray<FString>& Data, int32& Total, FString Delimiter = ",", bool HeaderFirst = true);
// config ini
static bool WriteConfigFile(FString Filename, FString Section, FString Key, FProperty* Type, void* Value, bool SingleLineArray, bool bWriteToDisk);
static bool ReadConfigFile(FString Filename, FString Section, FString Key, FProperty* Type, void* Value, bool SingleLineArray, bool bLoadFromDisk);
static bool WriteConfigFile(FString Filename, FString Section, FString Key, FProperty* Type, void* Value, bool SingleLineArray);
static bool ReadConfigFile(FString Filename, FString Section, FString Key, FProperty* Type, void* Value, bool SingleLineArray);
static void FindOrCreateConfigFile(const FString& InFilePath, FConfigFile& OutConfigFile);
static bool SaveConfigFile(const FString& InFilePath);
// datatable csv
static bool WriteTableToCSV(const UDataTable& InDataTable, FString& Output);
static bool WriteRowToCSV(const UScriptStruct* InRowStruct, const void* InRowData, FString& ExportedText);

View File

@ -1,37 +1,53 @@
// Copyright 2023 RLoris
// Copyright 2025 RLoris
#pragma once
#include "Async/Future.h"
#include "Engine/Texture2D.h"
#include "Engine/World.h"
#include "Kismet/BlueprintAsyncActionBase.h"
#include "Misc/Paths.h"
#include "FileHelperScreenshotAction.generated.h"
namespace ECameraProjectionMode
{
enum Type : int;
}
USTRUCT(BlueprintType)
struct FFileHelperScreenshotActionOptions
{
GENERATED_BODY()
/** File name without extension or path information */
UPROPERTY(BlueprintReadWrite, Category = "Screenshot")
/** Directory path where to store screenshot */
UPROPERTY(BlueprintReadWrite, Category = "FileHelper|Screenshot")
FString DirectoryPath = FPaths::ScreenShotDir();
/** File name without extension or path information, extension will be added internally (.png or .exr) */
UPROPERTY(BlueprintReadWrite, Category = "FileHelper|Screenshot")
FString Filename;
/** Prefix filename with a custom timestamp */
UPROPERTY(BlueprintReadWrite, Category = "Screenshot")
UPROPERTY(BlueprintReadWrite, Category = "FileHelper|Screenshot")
bool bPrefixTimestamp = true;
/** Include the UI in the screenshot */
UPROPERTY(BlueprintReadWrite, Category = "Screenshot")
/** Include the viewport UI in the screenshot, only used when CustomCameraActor is not provided */
UPROPERTY(BlueprintReadWrite, Category = "FileHelper|Screenshot")
bool bShowUI = false;
/** Uses this option only if the scene has HDR enabled,
* extension of screenshot file will be exr instead of png */
UPROPERTY(BlueprintReadWrite, Category = "Screenshot")
/**
* Uses this option only if the scene has HDR enabled,
* extension of screenshot file will be exr instead of png,
* if the scene is not using HDR, fallback to png
*/
UPROPERTY(BlueprintReadWrite, Category = "FileHelper|Screenshot")
bool bWithHDR = false;
/* Leave this empty for default screenshot,
* a different type of screenshot will be taken with a render target if set,
* options and settings quality may differ from regular screenshot, no UI shown */
/**
* Leave this empty for default viewport screenshot,
* if set, a different type of screenshot will be taken from a different perspective,
* options and settings quality may differ from regular screenshot, no UI is shown
*/
UPROPERTY(BlueprintReadWrite, Category = "Screenshot")
TObjectPtr<ACameraActor> CustomCameraActor = nullptr;
};
@ -43,43 +59,52 @@ class FILEHELPER_API UFileHelperScreenshotAction : public UBlueprintAsyncActionB
public:
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOutputPin, UTexture2D*, Screenshot, FString, Path);
UPROPERTY(BlueprintAssignable)
FOutputPin Completed;
UPROPERTY(BlueprintAssignable)
FOutputPin Failed;
UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true", WorldContext = "InWorldContextObject", Keywords = "File plugin screenshot save load texture", ToolTip = "Take a screenshot, save and load it"), Category = "Screenshot")
static UFileHelperScreenshotAction* TakeScreenshot(UObject* InWorldContextObject, const FFileHelperScreenshotActionOptions& InOptions);
UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true", Keywords = "File plugin screenshot save load texture", ToolTip = "Take a screenshot, save and load it"), Category = "FileHelper|Screenshot")
static UFileHelperScreenshotAction* TakeScreenshot(const FFileHelperScreenshotActionOptions& InOptions);
UFUNCTION(BlueprintCallable, meta = (Keywords = "screenshot load texture FileHelper", ToolTip = "Load a screenshot into a texture"), Category = "Screenshot")
UFUNCTION(BlueprintCallable, meta = (Keywords = "screenshot load texture FileHelper", ToolTip = "Load a screenshot into a texture"), Category = "FileHelper|Screenshot")
static UTexture2D* LoadScreenshot(const FString& InFilePath);
virtual void Activate() override;
private:
UFUNCTION()
//~ Begin UBlueprintAsyncActionBase
virtual void Activate() override;
//~ End UBlueprintAsyncActionBase
void OnTaskCompleted();
void OnTaskFailed();
void CreateCustomCameraScreenshot();
void CreateViewportScreenshot();
void CreatePlayerPOVScreenshot();
void CreateRenderTargetScreenshot(UWorld* InWorld, float InFOV, ECameraProjectionMode::Type InProjectionMode, float InOrthoWidth, const FVector& InLocation, const FRotator& InRotation, int32 InWidth, int32 InHeight);
void ConvertLinearColorToColorBuffer(const TArray<FLinearColor>& InSourceBuffer, TArray<FColor>& OutDestBuffer);
bool WriteColorBufferToDisk(const TArray<FColor>& InBuffer, int32 InWidth, int32 InHeight, UTexture2D*& OutTexture) const;
TFuture<bool> WriteLinearColorBufferToDiskAsync(TArray<FLinearColor>&& InBuffer, int32 InWidth, int32 InHeight);
TFuture<bool> WriteColorBufferToDiskAsync(TArray<FColor>&& InBuffer, int32 InWidth, int32 InHeight);
void Reset();
UPROPERTY()
TObjectPtr<UObject> WorldContextObject;
/** Final screenshot texture */
UPROPERTY()
TObjectPtr<UTexture2D> ScreenshotTexture;
/** Screenshot options */
UPROPERTY()
FFileHelperScreenshotActionOptions Options;
/** The file path of the new screenshot taken */
UPROPERTY()
FString FilePath;
/** Is this node active */
UPROPERTY()
bool bActive = false;
};