// Copyright 2023 RLoris #include "FileHelperBPLibrary.h" #include "HAL/PlatformFileManager.h" #include "HAL/FileManager.h" #include "Misc/FileHelper.h" #include "Misc/Base64.h" #include "Math/Color.h" #include "Misc/ConfigCacheIni.h" #include "Engine/DataTable.h" #include "Internationalization/Regex.h" #include "Serialization/Csv/CsvParser.h" #include "UObject/TextProperty.h" class FCustomFileVisitor : public IPlatformFile::FDirectoryVisitor { public: FString BasePath; TArray& Nodes; FString Filter; FRegexPattern CustomPattern; bool bFile = true; bool bDirectory = true; FCustomFileVisitor(FString& Path, TArray& 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() { FEnginePath P; P.Directory = FPaths::ConvertRelativePathToFull(FPaths::EngineDir()); P.Config = FPaths::ConvertRelativePathToFull(FPaths::EngineConfigDir()); P.Content = FPaths::ConvertRelativePathToFull(FPaths::EngineContentDir()); P.Intermediate = FPaths::ConvertRelativePathToFull(FPaths::EngineIntermediateDir()); P.Plugins = FPaths::ConvertRelativePathToFull(FPaths::EnginePluginsDir()); P.Saved =FPaths::ConvertRelativePathToFull( FPaths::EngineSavedDir()); P.User = FPaths::ConvertRelativePathToFull(FPaths::EngineUserDir()); P.DefaultLayout = FPaths::ConvertRelativePathToFull(FPaths::EngineDefaultLayoutDir()); P.PlatformExtensions = FPaths::ConvertRelativePathToFull(FPaths::EnginePlatformExtensionsDir()); P.UserLayout = FPaths::ConvertRelativePathToFull(FPaths::EngineUserLayoutDir()); return P; } FProjectPath UFileHelperBPLibrary::GetProjectDirectories() { FProjectPath P; P.Directory = FPaths::ConvertRelativePathToFull(FPaths::ProjectDir()); P.Config = FPaths::ConvertRelativePathToFull(FPaths::ProjectConfigDir()); P.Content = FPaths::ConvertRelativePathToFull(FPaths::ProjectContentDir()); P.Intermediate = FPaths::ConvertRelativePathToFull(FPaths::ProjectIntermediateDir()); P.Log = FPaths::ConvertRelativePathToFull(FPaths::ProjectLogDir()); P.Mods = FPaths::ConvertRelativePathToFull(FPaths::ProjectModsDir()); P.Plugins = FPaths::ConvertRelativePathToFull(FPaths::ProjectPluginsDir()); P.Saved = FPaths::ConvertRelativePathToFull(FPaths::ProjectSavedDir()); P.User = FPaths::ConvertRelativePathToFull(FPaths::ProjectUserDir()); P.PersistentDownload = FPaths::ConvertRelativePathToFull(FPaths::ProjectPersistentDownloadDir()); P.PlatformExtensions = FPaths::ConvertRelativePathToFull(FPaths::ProjectPlatformExtensionsDir()); return P; } bool UFileHelperBPLibrary::ReadText(FString Path, FString& Output) { IPlatformFile& FileManager = FPlatformFileManager::Get().GetPlatformFile(); if (FileManager.FileExists(*Path)) { return FFileHelper::LoadFileToString(Output, *Path); } return false; } bool UFileHelperBPLibrary::SaveText(FString Path, FString Text, FString& Error, bool Append, bool Force) { IPlatformFile& FileManager = FPlatformFileManager::Get().GetPlatformFile(); FText ErrorFilename; if (!FFileHelper::IsFilenameValidForSaving(Path, ErrorFilename)) { Error = FString("Filename is not valid"); return false; } if (!FileManager.FileExists(*Path) || Append || Force) { return FFileHelper::SaveStringToFile(Text, *Path, FFileHelper::EEncodingOptions::AutoDetect, &IFileManager::Get(), Append ? FILEWRITE_Append : FILEWRITE_None); } else { Error = FString("File already exists"); } return false; } bool UFileHelperBPLibrary::SaveCSV(FString Path, TArray Headers, TArray Data, int32& Total, bool Force) { FString Output; if (!UFileHelperBPLibrary::CSVToString(Output, Headers, Data, Total)) { return false; } FString Error; return UFileHelperBPLibrary::SaveText(Path, Output, Error, false, Force); } bool UFileHelperBPLibrary::ReadCSV(FString Path, TArray& Headers, TArray& Data, int32& Total, bool HeaderFirst) { Total = 0; IPlatformFile& FileManager = FPlatformFileManager::Get().GetPlatformFile(); if (!FileManager.FileExists(*Path)) { return false; } FString Result; if (!FFileHelper::LoadFileToString(Result, *Path)) { return false; } return UFileHelperBPLibrary::StringToCSV(Result, Headers, Data, Total, HeaderFirst); // return UFileHelperBPLibrary::StringArrayToCSV(Result, Headers, Data, Total, ",", HeaderFirst); } bool UFileHelperBPLibrary::ReadLine(FString Path, FString Pattern, TArray& Lines) { IPlatformFile& FileManager = FPlatformFileManager::Get().GetPlatformFile(); if (!FileManager.FileExists(*Path)) { return false; } if (!Pattern.IsEmpty()) { FRegexPattern CustomPattern(Pattern); return FFileHelper::LoadFileToStringArrayWithPredicate(Lines, *Path, [CustomPattern](const FString& Line) { FRegexMatcher CustomMatcher(CustomPattern, Line); return CustomMatcher.FindNext(); }); } return FFileHelper::LoadFileToStringArray(Lines, *Path); } bool UFileHelperBPLibrary::ReadLineRange(FString Path, TArray& Lines, int32 StartIdx, int32 EndIdx) { if (EndIdx != INDEX_NONE && EndIdx < StartIdx) { return false; } IPlatformFile& FileManager = FPlatformFileManager::Get().GetPlatformFile(); if (!FileManager.FileExists(*Path)) { return false; } int32 LineCount = 0; return FFileHelper::LoadFileToStringArrayWithPredicate(Lines, *Path, [&LineCount, StartIdx, EndIdx](const FString& Line)->bool{ const bool bRangeStart = LineCount >= StartIdx; const bool bRangeEnd = EndIdx == INDEX_NONE || LineCount < EndIdx; LineCount++; return bRangeStart && bRangeEnd; }); } bool UFileHelperBPLibrary::SaveLine(FString Path, const TArray& Text, FString& Error, bool Append, bool Force) { IPlatformFile& FileManager = FPlatformFileManager::Get().GetPlatformFile(); FText ErrorFilename; if (!FFileHelper::IsFilenameValidForSaving(Path, ErrorFilename)) { Error = FString("Filename is not valid"); return false; } if (!FileManager.FileExists(*Path) || Append || Force) { return FFileHelper::SaveStringArrayToFile(Text, *Path, FFileHelper::EEncodingOptions::AutoDetect, &IFileManager::Get(), Append ? FILEWRITE_Append : FILEWRITE_None); } else { Error = FString("File already exists"); } return false; } bool UFileHelperBPLibrary::ReadByte(FString Path, TArray& Bytes) { IPlatformFile& FileManager = FPlatformFileManager::Get().GetPlatformFile(); if (!FileManager.FileExists(*Path)) { return false; } return FFileHelper::LoadFileToArray(Bytes, *Path); } FString UFileHelperBPLibrary::BytesToBase64(const TArray Bytes) { return FBase64::Encode(Bytes); } bool UFileHelperBPLibrary::BytesFromBase64(const FString Source, TArray& Out) { return FBase64::Decode(Source, Out); } bool UFileHelperBPLibrary::SaveByte(FString Path, const TArray& Bytes, FString& Error, bool Append, bool Force) { IPlatformFile& file = FPlatformFileManager::Get().GetPlatformFile(); FText ErrorFilename; if (!FFileHelper::IsFilenameValidForSaving(Path, ErrorFilename)) { Error = FString("Filename is not valid"); return false; } if (!file.FileExists(*Path) || Append || Force) { return FFileHelper::SaveArrayToFile(Bytes, *Path, &IFileManager::Get(), Append ? FILEWRITE_Append : FILEWRITE_None); } else { Error = FString("File already exists"); } return false; } bool UFileHelperBPLibrary::StringToCSV(FString Content, TArray& Headers, TArray& Data, int32& Total, bool HeaderFirst) { const FCsvParser Parser(Content); TArray> Rows = Parser.GetRows(); for (TArray Row : Rows) { Total++; for (FString Col : Row) { if (Total == 1 && HeaderFirst) { Headers.Add(Col); } else { Data.Add(Col); } } } return true; // auto Result = SplitString(Content, LINE_TERMINATOR, ESearchCase::Type::IgnoreCase); // return UFileHelperBPLibrary::StringArrayToCSV(Result, Headers, Data, Total, HeaderFirst); } bool UFileHelperBPLibrary::CSVToString(FString& Output, TArray Headers, TArray Data, int32& Total) { Total = 0; FString Delimiter = ","; if (Headers.Num() == 0) { return false; } if (Data.Num() % Headers.Num() != 0) { return false; } Output = TEXT(""); // Header row for (FString Col : Headers) { if (Output.Len() > 0) { Output += Delimiter; } Output += (TEXT("\"") + Col.Replace(TEXT("\""), TEXT("\"\"")) + TEXT("\"")); } Output += LINE_TERMINATOR; FString Row = TEXT(""); int32 Count = 0; // Data row for (FString Col : Data) { Count++; if (Row.Len() > 0) { Row += Delimiter; } Row += (TEXT("\"") + Col.Replace(TEXT("\""), TEXT("\"\"")) + TEXT("\"")); if (Count % Headers.Num() == 0) { Row += LINE_TERMINATOR; Output += Row; Row = ""; } } Total = (Data.Num() / Headers.Num()) + 1; return true; } bool UFileHelperBPLibrary::StringArrayToCSV(TArray Lines, TArray& Headers, TArray& Data, int32& Total, FString Delimiter, bool HeaderFirst) { for (auto Line : Lines) { Total++; if (!Line.Contains(TEXT("\"") + Delimiter + TEXT("\""))) { continue; } if (Total == 1 && HeaderFirst) { for (FString Col : UFileHelperBPLibrary::SplitString(Line, TEXT("\"") + Delimiter + TEXT("\""), ESearchCase::CaseSensitive)) { Col.TrimQuotesInline(); Col.ReplaceInline(TEXT("\"\""), TEXT("\"")); Headers.Add(Col); } } else { for (FString Col : UFileHelperBPLibrary::SplitString(Line, TEXT("\"") + Delimiter + TEXT("\""), ESearchCase::CaseSensitive)) { Col.TrimQuotesInline(); Col.ReplaceInline(TEXT("\"\""), TEXT("\"")); Data.Add(Col); } } } return true; } bool UFileHelperBPLibrary::IsFile(FString Path) { IPlatformFile& FileManager = FPlatformFileManager::Get().GetPlatformFile(); return FileManager.FileExists(*Path); } bool UFileHelperBPLibrary::IsDirectory(FString Path) { IPlatformFile& FileManager = FPlatformFileManager::Get().GetPlatformFile(); return FileManager.DirectoryExists(*Path); } bool UFileHelperBPLibrary::IsValidFilename(FString Filename) { FText Error; return FFileHelper::IsFilenameValidForSaving(*Filename, Error); } bool UFileHelperBPLibrary::IsValidPath(FString Path) { return FPaths::ValidatePath(Path); } bool UFileHelperBPLibrary::ValidateFilename(FString Filename, FString& ValidName) { ValidName = FPaths::MakeValidFileName(Filename); return IsValidFilename(ValidName); } bool UFileHelperBPLibrary::SetReadOnlyFlag(FString FilePath, bool Flag) { IPlatformFile& FileManager = FPlatformFileManager::Get().GetPlatformFile(); return FileManager.SetReadOnly(*FilePath, Flag); } bool UFileHelperBPLibrary::GetReadOnlyFlag(FString FilePath) { IPlatformFile& FileManager = FPlatformFileManager::Get().GetPlatformFile(); return FileManager.IsReadOnly(*FilePath); } int64 UFileHelperBPLibrary::GetFileSize(FString FilePath) { IPlatformFile& FileManager = FPlatformFileManager::Get().GetPlatformFile(); return FileManager.FileSize(*FilePath); } bool FCustomFileVisitor::Visit(const TCHAR* FilenameOrDirectory, bool bIsDirectory) { if ((bFile && !bIsDirectory) || (bDirectory && bIsDirectory)) { FString RelativePath = FString(FilenameOrDirectory); FPaths::MakePathRelativeTo(RelativePath, *BasePath); if (!Filter.IsEmpty()) { FRegexMatcher CustomMatcher(CustomPattern, RelativePath); if (CustomMatcher.FindNext()) { Nodes.Add(RelativePath); } } else { Nodes.Add(RelativePath); } } return true; } bool UFileHelperBPLibrary::ListDirectory(FString Path, FString Pattern, TArray& Nodes, bool ShowFile, bool ShowDirectory, bool Recursive) { IPlatformFile& FileManager = FPlatformFileManager::Get().GetPlatformFile(); 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 { return FileManager.IterateDirectory(*Path, CustomFileVisitor); } } bool UFileHelperBPLibrary::MakeDirectory(FString Path, bool Recursive) { IPlatformFile& FileManager = FPlatformFileManager::Get().GetPlatformFile(); if (FileManager.DirectoryExists(*Path)) { return true; } if (Recursive) { return FileManager.CreateDirectoryTree(*Path); } else { return FileManager.CreateDirectory(*Path); } } bool UFileHelperBPLibrary::RemoveDirectory(FString Path, bool Recursive) { IPlatformFile& FileManager = FPlatformFileManager::Get().GetPlatformFile(); if (!FileManager.DirectoryExists(*Path)) { return true; } if (Recursive) { return FileManager.DeleteDirectoryRecursively(*Path); } else { return FileManager.DeleteDirectory(*Path); } } bool UFileHelperBPLibrary::CopyDirectory(FString Source, FString Dest) // bool Force = false { IPlatformFile& FileManager = FPlatformFileManager::Get().GetPlatformFile(); FPaths::NormalizeDirectoryName(Dest); if (!FileManager.DirectoryExists(*Source) && !FileManager.FileExists(*Source)) { return false; } if (!FileManager.DirectoryExists(*Dest)) { return false; } return FileManager.CopyDirectoryTree(*Dest, *Source, true); } bool UFileHelperBPLibrary::MoveDirectory(FString Source, FString Dest) // bool Force = false { FPaths::NormalizeDirectoryName(Source); FPaths::NormalizeDirectoryName(Dest); if (Dest.Equals(Source)) { return true; } if (!UFileHelperBPLibrary::CopyDirectory(Source, Dest)) { return false; } return UFileHelperBPLibrary::RemoveDirectory(Source, true); } bool UFileHelperBPLibrary::NodeStats(FString Path, FCustomNodeStat& Stats) { IPlatformFile& FileManager = FPlatformFileManager::Get().GetPlatformFile(); if (!FileManager.DirectoryExists(*Path) && !FileManager.FileExists(*Path)) { return false; } const FFileStatData Data = FileManager.GetStatData(*Path); if (!Data.bIsValid) { return false; } Stats.CreationTime = Data.CreationTime; Stats.FileSize = Data.FileSize; Stats.IsDirectory = Data.bIsDirectory; Stats.IsReadOnly = Data.bIsReadOnly; Stats.LastAccessTime = Data.AccessTime; Stats.ModificationTime = Data.ModificationTime; return true; } bool UFileHelperBPLibrary::RemoveFile(FString Path) { IPlatformFile& FileManager = FPlatformFileManager::Get().GetPlatformFile(); if (FileManager.DirectoryExists(*Path)) { return false; } if (!FileManager.FileExists(*Path)) { return true; } return FileManager.DeleteFile(*Path); } bool UFileHelperBPLibrary::CopyFile(FString Source, FString Dest, bool Force) { FPaths::NormalizeFilename(Source); FPaths::NormalizeFilename(Dest); if (Dest.Equals(Source)) { return true; } FText Error; if (!FFileHelper::IsFilenameValidForSaving(*Dest, Error)) { return false; } IPlatformFile& FileManager = FPlatformFileManager::Get().GetPlatformFile(); if (!FileManager.FileExists(*Source)) { return false; } if (!Force && FileManager.FileExists(*Dest)) { return false; } UFileHelperBPLibrary::RemoveFile(Dest); return FileManager.CopyFile(*Dest, *Source); } bool UFileHelperBPLibrary::MoveFile(FString Source, FString Dest, bool Force) { FPaths::NormalizeFilename(Source); FPaths::NormalizeFilename(Dest); if (Dest.Equals(Source)) { return true; } FText Error; if (!FFileHelper::IsFilenameValidForSaving(*Dest, Error)) { return false; } IPlatformFile& FileManager = FPlatformFileManager::Get().GetPlatformFile(); if (!FileManager.FileExists(*Source)) { return false; } if (!Force && FileManager.FileExists(*Dest)) { return false; } UFileHelperBPLibrary::RemoveFile(Dest); return FileManager.MoveFile(*Dest, *Source); } bool UFileHelperBPLibrary::RenameFile(FString Path, FString NewName) { FText Error; if (!FFileHelper::IsFilenameValidForSaving(*NewName, Error)) { return false; } IPlatformFile& FileManager = FPlatformFileManager::Get().GetPlatformFile(); if (!FileManager.FileExists(*Path)) { return false; } FString Clean = FPaths::GetCleanFilename(NewName); FString Base = FPaths::GetPath(Path); FString Output = FPaths::Combine(*Base, *Clean); if (FileManager.FileExists(*Output) || FileManager.DirectoryExists(*Output)) { return false; } return FileManager.MoveFile(*Output, *Path); } void UFileHelperBPLibrary::GetPathParts(FString Path, FString& PathPart, FString& BasePart, FString& ExtensionPart, FString& FileName) { PathPart = FPaths::GetPath(Path); BasePart = FPaths::GetBaseFilename(Path); ExtensionPart = FPaths::GetExtension(Path); FileName = FPaths::GetCleanFilename(Path); } bool UFileHelperBPLibrary::DatatableToCSV(UDataTable* Table, FString& Output) { if (Table == nullptr || !Table->RowStruct) { return false; } // See Table->GetTableAsCSV return UFileHelperBPLibrary::WriteTableToCSV(*Table, Output); } bool UFileHelperBPLibrary::DataTableToJSON(UDataTable* Table, FString& Output) { if (Table == nullptr || !Table->RowStruct) { return false; } // See Table->GetTableAsJSON return UFileHelperBPLibrary::WriteTableToJSON(*Table, Output); } UDataTable* UFileHelperBPLibrary::CSVToDataTable(FString CSV, UScriptStruct* Struct, bool& Success) { Success = false; if (Struct == nullptr) { return nullptr; } UDataTable* DataTable = NewObject(); DataTable->RowStruct = Struct; auto Result = DataTable->CreateTableFromCSVString(CSV); if (Result.Num() == 0) { Success = true; } return DataTable; } UDataTable* UFileHelperBPLibrary::JSONToDataTable(FString JSON, UScriptStruct* Struct, bool& Success) { Success = false; if (Struct == nullptr) { return nullptr; } UDataTable* DataTable = NewObject(); DataTable->RowStruct = Struct; auto Result = DataTable->CreateTableFromJSONString(JSON); if (Result.Num() == 0) { Success = true; } return DataTable; } void UFileHelperBPLibrary::ReadConfig(FString FilePath, FString Section, FString Key, bool& Success, bool SingleLineArrayRead, UStruct*& OutValue, bool bLoadFromDisk) { checkNoEntry(); } TArray UFileHelperBPLibrary::SplitString(FString String, FString Separator, ESearchCase::Type SearchCase) { FString LeftString; FString RightString; TArray Array; bool Split = false; do { Split = String.Split(Separator, &LeftString, &RightString, SearchCase); if (Split) { Array.Add(LeftString); } else { Array.Add(String); } String = RightString; } while (Split); return Array; } bool UFileHelperBPLibrary::WriteConfigFile(FString Filename, FString Section, FString Key, FProperty* Type, void* Value, bool SingleLineArray, bool bWriteToDisk) { if (!GConfig) { return false; } if (FBoolProperty* BoolProperty = CastField(Type)) { GConfig->SetBool(*Section, *Key, *(static_cast(Value)), Filename); } else if (FIntProperty* IntProperty = CastField(Type)) { GConfig->SetInt(*Section, *Key, *(static_cast(Value)), Filename); } else if (FStrProperty* StrProperty = CastField(Type)) { GConfig->SetString(*Section, *Key, **(static_cast(Value)), Filename); } else if (FFloatProperty* FloatProperty = CastField(Type)) { GConfig->SetFloat(*Section, *Key, *(static_cast(Value)), Filename); } else if (FDoubleProperty* DoubleProperty = CastField(Type)) { GConfig->SetDouble(*Section, *Key, *(static_cast(Value)), Filename); } else if (FArrayProperty* ArrayProperty = CastField(Type)) { if (FStrProperty* ArrayInnerProperty = CastField(ArrayProperty->Inner)) { TArray* Arr = (static_cast*>(Value)); if (SingleLineArray) { GConfig->SetSingleLineArray(*Section, *Key, *Arr, Filename); } else { GConfig->SetArray(*Section, *Key, *Arr, Filename); } } else { return false; } } else if (FTextProperty* TextProperty = CastField(Type)) { GConfig->SetText(*Section, *Key, *(static_cast(Value)), Filename); } else if (FStructProperty* StructProperty = CastField(Type)) { if (StructProperty->Struct->IsNative()) { static const FName RotatorType = TEXT("Rotator"); static const FName VectorType = TEXT("Vector"); static const FName LinearColorType = TEXT("LinearColor"); static const FName Vector4Type = TEXT("Vector4"); static const FName Vector2DType = TEXT("Vector2D"); const FName TypeName = StructProperty->Struct->GetFName(); if (TypeName == RotatorType) { GConfig->SetRotator(*Section, *Key, *(static_cast(Value)), *Filename); } else if (TypeName == VectorType) { GConfig->SetVector(*Section, *Key, *(static_cast(Value)), *Filename); } else if (TypeName == LinearColorType) { GConfig->SetColor(*Section, *Key, *(static_cast(Value)), *Filename); } else if (TypeName == Vector4Type) { GConfig->SetVector4(*Section, *Key, *(static_cast(Value)), *Filename); } else if (TypeName == Vector2DType) { GConfig->SetVector2D(*Section, *Key, *(static_cast(Value)), *Filename); } else { return false; } } else { return false; } } else { return false; } if (bWriteToDisk) { GConfig->Flush(false, Filename); } return true; } bool UFileHelperBPLibrary::ReadConfigFile(FString Filename, FString Section, FString Key, FProperty* Type, void* Value, bool SingleLineArray, bool bLoadFromDisk) { if (!GConfig) { return false; } if (bLoadFromDisk) { FConfigFile* ConfigFile = GConfig->FindConfigFile(Filename); if (!ConfigFile) { return false; } ConfigFile->Read(Filename); } bool Success = false; if (FBoolProperty* BoolProperty = CastField(Type)) { Success = GConfig->GetBool(*Section, *Key, *(static_cast(Value)), Filename); } else if (FIntProperty* IntProperty = CastField(Type)) { Success = GConfig->GetInt(*Section, *Key, *(static_cast(Value)), Filename); } else if (FStrProperty* StrProperty = CastField(Type)) { Success = GConfig->GetString(*Section, *Key, *(static_cast(Value)), Filename); } else if (FFloatProperty* FloatProperty = CastField(Type)) { Success = GConfig->GetFloat(*Section, *Key, *(static_cast(Value)), Filename); } else if (FDoubleProperty* DoubleProperty = CastField(Type)) { Success = GConfig->GetDouble(*Section, *Key, *(static_cast(Value)), Filename); } else if (FArrayProperty* ArrayProperty = CastField(Type)) { if (FStrProperty* ArrayInnerProperty = CastField(ArrayProperty->Inner)) { TArray* Arr = (static_cast*>(Value)); if (SingleLineArray) { Success = (GConfig->GetSingleLineArray(*Section, *Key, *Arr, Filename) != 0); } else { Success = (GConfig->GetArray(*Section, *Key, *Arr, Filename) != 0); } } } else if (FTextProperty* TextProperty = CastField(Type)) { Success = GConfig->GetText(*Section, *Key, *(static_cast(Value)), Filename); } else if (FStructProperty* StructProperty = CastField(Type)) { if (StructProperty->Struct->IsNative()) { static const FName RotatorType = TEXT("Rotator"); static const FName VectorType = TEXT("Vector"); static const FName LinearColorType = TEXT("LinearColor"); static const FName Vector4Type = TEXT("Vector4"); static const FName Vector2DType = TEXT("Vector2D"); const FName TypeName = StructProperty->Struct->GetFName(); if (TypeName == RotatorType) { Success = GConfig->GetRotator(*Section, *Key, *(static_cast(Value)), *Filename); } else if (TypeName == VectorType) { Success = GConfig->GetVector(*Section, *Key, *(static_cast(Value)), *Filename); } else if (TypeName == LinearColorType) { Success = GConfig->GetColor(*Section, *Key, *(static_cast(Value)), *Filename); } else if (TypeName == Vector4Type) { Success = GConfig->GetVector4(*Section, *Key, *(static_cast(Value)), *Filename); } else if (TypeName == Vector2DType) { Success = GConfig->GetVector2D(*Section, *Key, *(static_cast(Value)), *Filename); } } } return Success; } void UFileHelperBPLibrary::WriteConfig(FString FilePath, FString Section, FString Key, bool& Success, bool SingleLineArrayWrite, const UStruct* Value, bool bWriteToDisk) { checkNoEntry(); } bool UFileHelperBPLibrary::RemoveConfig(FString FilePath, FString Section, FString Key, bool bWriteToDisk) { if (!GConfig) { return false; } if (!GConfig->RemoveKey(*Section, *Key, *FilePath)) { return false; } if (bWriteToDisk) { GConfig->Flush(false, FilePath); } return true; } // equivalent GetTableAsCSV() bool UFileHelperBPLibrary::WriteTableToCSV(const UDataTable& InDataTable, FString& ExportedText) { if (!InDataTable.RowStruct) { return false; } // Write the header (column titles) FString ImportKeyField; if (!InDataTable.ImportKeyField.IsEmpty()) { // Write actual name if we have it ImportKeyField = InDataTable.ImportKeyField; ExportedText += ImportKeyField; } else { ExportedText += TEXT("---"); } FProperty* SkipProperty = nullptr; for (TFieldIterator It(InDataTable.RowStruct); It; ++It) { FProperty* BaseProp = *It; check(BaseProp); FString ColumnHeader = DataTableUtils::GetPropertyExportName(BaseProp, EDataTableExportFlags::None); if (ColumnHeader == ImportKeyField) { // Don't write header again if this is the name field, and save for skipping later SkipProperty = BaseProp; continue; } ExportedText += TEXT(","); ExportedText += ColumnHeader; } ExportedText += TEXT("\n"); // Write each row for (auto RowIt = InDataTable.GetRowMap().CreateConstIterator(); RowIt; ++RowIt) { FName RowName = RowIt.Key(); ExportedText += RowName.ToString(); uint8* RowData = RowIt.Value(); UFileHelperBPLibrary::WriteRowToCSV(InDataTable.RowStruct, RowData, ExportedText); ExportedText += TEXT("\n"); } return true; } bool UFileHelperBPLibrary::WriteRowToCSV(const UScriptStruct* InRowStruct, const void* InRowData, FString& ExportedText) { if (!InRowStruct) { return false; } for (TFieldIterator It(InRowStruct); It; ++It) { FProperty* BaseProp = *It; check(BaseProp); const void* Data = BaseProp->ContainerPtrToValuePtr(InRowData, 0); UFileHelperBPLibrary::WriteStructEntryToCSV(InRowData, BaseProp, Data, ExportedText); } return true; } bool UFileHelperBPLibrary::WriteStructEntryToCSV(const void* InRowData, FProperty* InProperty, const void* InPropertyData, FString& ExportedText) { ExportedText += TEXT(","); const FString PropertyValue = DataTableUtils::GetPropertyValueAsString(InProperty, (uint8*)InRowData, EDataTableExportFlags::None); ExportedText += TEXT("\""); ExportedText += PropertyValue.Replace(TEXT("\""), TEXT("\"\"")); ExportedText += TEXT("\""); return true; } // equivalent GetTableAsJSON() FString UFileHelperBPLibrary::GetKeyFieldName(const UDataTable& InDataTable) { FString ExplicitString = InDataTable.ImportKeyField; if (ExplicitString.IsEmpty()) { return TEXT("Name"); } else { return ExplicitString; } } bool UFileHelperBPLibrary::WriteTableToJSON(const UDataTable& InDataTable, FString& OutExportText) { if (!InDataTable.RowStruct) { return false; } TSharedRef>> JsonWriter = TJsonWriterFactory>::Create(&OutExportText); FString KeyField = UFileHelperBPLibrary::GetKeyFieldName(InDataTable); JsonWriter->WriteArrayStart(); // Iterate over rows for (auto RowIt = InDataTable.GetRowMap().CreateConstIterator(); RowIt; ++RowIt) { JsonWriter->WriteObjectStart(); { // RowName const FName RowName = RowIt.Key(); JsonWriter->WriteValue(KeyField, RowName.ToString()); // Now the values uint8* RowData = RowIt.Value(); UFileHelperBPLibrary::WriteRowToJSON(InDataTable.RowStruct, RowData, JsonWriter); } JsonWriter->WriteObjectEnd(); } JsonWriter->WriteArrayEnd(); JsonWriter->Close(); return true; } bool UFileHelperBPLibrary::WriteTableAsObjectToJSON(const UDataTable& InDataTable, TSharedRef>> JsonWriter) { if (!InDataTable.RowStruct) { return false; } JsonWriter->WriteObjectStart(InDataTable.GetName()); // Iterate over rows for (auto RowIt = InDataTable.GetRowMap().CreateConstIterator(); RowIt; ++RowIt) { // RowName const FName RowName = RowIt.Key(); JsonWriter->WriteObjectStart(RowName.ToString()); { // Now the values uint8* RowData = RowIt.Value(); UFileHelperBPLibrary::WriteRowToJSON(InDataTable.RowStruct, RowData, JsonWriter); } JsonWriter->WriteObjectEnd(); } JsonWriter->WriteObjectEnd(); return true; } bool UFileHelperBPLibrary::WriteRowToJSON(const UScriptStruct* InRowStruct, const void* InRowData, TSharedRef>> JsonWriter) { if (!InRowStruct) { return false; } return UFileHelperBPLibrary::WriteStructToJSON(InRowStruct, InRowData, JsonWriter); } bool UFileHelperBPLibrary::WriteStructToJSON(const UScriptStruct* InStruct, const void* InStructData, TSharedRef>> JsonWriter) { for (TFieldIterator It(InStruct); It; ++It) { const FProperty* BaseProp = *It; check(BaseProp); const FString Identifier = DataTableUtils::GetPropertyExportName(BaseProp, EDataTableExportFlags::UseJsonObjectsForStructs); if (BaseProp->ArrayDim == 1) { const void* Data = BaseProp->ContainerPtrToValuePtr(InStructData, 0); UFileHelperBPLibrary::WriteStructEntryToJSON(InStructData, BaseProp, Data, JsonWriter); } else { JsonWriter->WriteArrayStart(Identifier); for (int32 ArrayEntryIndex = 0; ArrayEntryIndex < BaseProp->ArrayDim; ++ArrayEntryIndex) { const void* Data = BaseProp->ContainerPtrToValuePtr(InStructData, ArrayEntryIndex); UFileHelperBPLibrary::WriteContainerEntryToJSON(BaseProp, Data, &Identifier, JsonWriter); } JsonWriter->WriteArrayEnd(); } } return true; } bool UFileHelperBPLibrary::WriteStructEntryToJSON(const void* InRowData, const FProperty* InProperty, const void* InPropertyData, TSharedRef>> JsonWriter) { const FString Identifier = DataTableUtils::GetPropertyExportName(InProperty, EDataTableExportFlags::UseJsonObjectsForStructs); if (const FEnumProperty* EnumProp = CastField(InProperty)) { const FString PropertyValue = DataTableUtils::GetPropertyValueAsString(EnumProp, (uint8*)InRowData, EDataTableExportFlags::UseJsonObjectsForStructs); JsonWriter->WriteValue(Identifier, PropertyValue); } else if (const FNumericProperty* NumProp = CastField(InProperty)) { if (NumProp->IsEnum()) { const FString PropertyValue = DataTableUtils::GetPropertyValueAsString(InProperty, (uint8*)InRowData, EDataTableExportFlags::UseJsonObjectsForStructs); JsonWriter->WriteValue(Identifier, PropertyValue); } else if (NumProp->IsInteger()) { const int64 PropertyValue = NumProp->GetSignedIntPropertyValue(InPropertyData); JsonWriter->WriteValue(Identifier, PropertyValue); } else { const double PropertyValue = NumProp->GetFloatingPointPropertyValue(InPropertyData); JsonWriter->WriteValue(Identifier, PropertyValue); } } else if (const FBoolProperty* BoolProp = CastField(InProperty)) { const bool PropertyValue = BoolProp->GetPropertyValue(InPropertyData); JsonWriter->WriteValue(Identifier, PropertyValue); } else if (const FArrayProperty* ArrayProp = CastField(InProperty)) { JsonWriter->WriteArrayStart(Identifier); FScriptArrayHelper ArrayHelper(ArrayProp, InPropertyData); for (int32 ArrayEntryIndex = 0; ArrayEntryIndex < ArrayHelper.Num(); ++ArrayEntryIndex) { const uint8* ArrayEntryData = ArrayHelper.GetRawPtr(ArrayEntryIndex); UFileHelperBPLibrary::WriteContainerEntryToJSON(ArrayProp->Inner, ArrayEntryData, &Identifier, JsonWriter); } JsonWriter->WriteArrayEnd(); } else if (const FSetProperty* SetProp = CastField(InProperty)) { JsonWriter->WriteArrayStart(Identifier); FScriptSetHelper SetHelper(SetProp, InPropertyData); for (int32 SetSparseIndex = 0; SetSparseIndex < SetHelper.GetMaxIndex(); ++SetSparseIndex) { if (SetHelper.IsValidIndex(SetSparseIndex)) { const uint8* SetEntryData = SetHelper.GetElementPtr(SetSparseIndex); UFileHelperBPLibrary::WriteContainerEntryToJSON(SetHelper.GetElementProperty(), SetEntryData, &Identifier, JsonWriter); } } JsonWriter->WriteArrayEnd(); } else if (const FMapProperty* MapProp = CastField(InProperty)) { JsonWriter->WriteObjectStart(Identifier); FScriptMapHelper MapHelper(MapProp, InPropertyData); for (int32 MapSparseIndex = 0; MapSparseIndex < MapHelper.GetMaxIndex(); ++MapSparseIndex) { if (MapHelper.IsValidIndex(MapSparseIndex)) { const uint8* MapKeyData = MapHelper.GetKeyPtr(MapSparseIndex); const uint8* MapValueData = MapHelper.GetValuePtr(MapSparseIndex); // JSON object keys must always be strings const FString KeyValue = DataTableUtils::GetPropertyValueAsStringDirect(MapHelper.GetKeyProperty(), (uint8*)MapKeyData, EDataTableExportFlags::UseJsonObjectsForStructs); UFileHelperBPLibrary::WriteContainerEntryToJSON(MapHelper.GetValueProperty(), MapValueData, &KeyValue, JsonWriter); } } JsonWriter->WriteObjectEnd(); } else if (const FStructProperty* StructProp = CastField(InProperty)) { JsonWriter->WriteObjectStart(Identifier); UFileHelperBPLibrary::WriteStructToJSON(StructProp->Struct, InPropertyData, JsonWriter); JsonWriter->WriteObjectEnd(); } else { const FString PropertyValue = DataTableUtils::GetPropertyValueAsString(InProperty, (uint8*)InRowData, EDataTableExportFlags::UseJsonObjectsForStructs); JsonWriter->WriteValue(Identifier, PropertyValue); } return true; } bool UFileHelperBPLibrary::WriteContainerEntryToJSON(const FProperty* InProperty, const void* InPropertyData, const FString* InIdentifier, TSharedRef>> JsonWriter) { if (const FEnumProperty* EnumProp = CastField(InProperty)) { const FString PropertyValue = DataTableUtils::GetPropertyValueAsStringDirect(InProperty, (uint8*)InPropertyData, EDataTableExportFlags::UseJsonObjectsForStructs); UFileHelperBPLibrary::WriteJSONValueWithOptionalIdentifier(JsonWriter, InIdentifier, *PropertyValue); } else if (const FNumericProperty* NumProp = CastField(InProperty)) { if (NumProp->IsEnum()) { const FString PropertyValue = DataTableUtils::GetPropertyValueAsStringDirect(InProperty, (uint8*)InPropertyData, EDataTableExportFlags::UseJsonObjectsForStructs); UFileHelperBPLibrary::WriteJSONValueWithOptionalIdentifier(JsonWriter, InIdentifier, *PropertyValue); } else if (NumProp->IsInteger()) { const int64 PropertyValue = NumProp->GetSignedIntPropertyValue(InPropertyData); UFileHelperBPLibrary::WriteJSONValueWithOptionalIdentifier(JsonWriter, InIdentifier, *FString::FromInt(PropertyValue)); } else { const double PropertyValue = NumProp->GetFloatingPointPropertyValue(InPropertyData); UFileHelperBPLibrary::WriteJSONValueWithOptionalIdentifier(JsonWriter, InIdentifier, *FString::SanitizeFloat(PropertyValue)); } } else if (const FBoolProperty* BoolProp = CastField(InProperty)) { const bool PropertyValue = BoolProp->GetPropertyValue(InPropertyData); UFileHelperBPLibrary::WriteJSONValueWithOptionalIdentifier(JsonWriter, InIdentifier, *(PropertyValue ? FString("true") : FString("false"))); } else if (const FStructProperty* StructProp = CastField(InProperty)) { UFileHelperBPLibrary::WriteJSONObjectStartWithOptionalIdentifier(JsonWriter, InIdentifier); UFileHelperBPLibrary::WriteStructToJSON(StructProp->Struct, InPropertyData, JsonWriter); JsonWriter->WriteObjectEnd(); } else if (const FArrayProperty* ArrayProp = CastField(InProperty)) { // Cannot nest arrays return false; } else if (const FSetProperty* SetProp = CastField(InProperty)) { // Cannot nest sets return false; } else if (const FMapProperty* MapProp = CastField(InProperty)) { // Cannot nest maps return false; } else { const FString PropertyValue = DataTableUtils::GetPropertyValueAsStringDirect(InProperty, (uint8*)InPropertyData, EDataTableExportFlags::UseJsonObjectsForStructs); UFileHelperBPLibrary::WriteJSONValueWithOptionalIdentifier(JsonWriter, InIdentifier, *PropertyValue); } return true; } void UFileHelperBPLibrary::WriteJSONObjectStartWithOptionalIdentifier(TSharedRef>> JsonWriter, const FString* InIdentifier) { if (InIdentifier) { JsonWriter->WriteObjectStart(*InIdentifier); } else { JsonWriter->WriteObjectStart(); } } void UFileHelperBPLibrary::WriteJSONValueWithOptionalIdentifier(TSharedRef>> JsonWriter, const FString* InIdentifier, const TCHAR* InValue) { if (InIdentifier) { JsonWriter->WriteValue(*InIdentifier, InValue); } else { JsonWriter->WriteValue(InValue); } }