Files
UnrealEngine/Engine/Source/Runtime/Online/BuildPatchServices/Private/Generation/FileAttributesParser.cpp
2025-05-18 13:04:45 +08:00

159 lines
5.3 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "Generation/FileAttributesParser.h"
#include "Misc/FileHelper.h"
#include "Templates/UniquePtr.h"
DECLARE_LOG_CATEGORY_EXTERN(LogFileAttributesParser, Log, All);
DEFINE_LOG_CATEGORY(LogFileAttributesParser);
namespace BuildPatchServices
{
FFileAttributes::FFileAttributes()
: bReadOnly(false)
, bCompressed(false)
, bUnixExecutable(false)
{}
class FFileAttributesParserImpl
: public FFileAttributesParser
{
typedef void(*SetKeyValueAttributeFunction)(FFileAttributes&, FString);
public:
FFileAttributesParserImpl(IPlatformFile& PlatformFile);
virtual ~FFileAttributesParserImpl();
virtual bool ParseFileAttributes(const FString& MetaFilename, TMap<FString, FFileAttributes>& FileAttributes) override;
private:
bool FileAttributesMetaToMap(const FString& AttributesList, TMap<FString, FFileAttributes>& FileAttributesMap);
private:
IPlatformFile& PlatformFile;
TMap<FString, SetKeyValueAttributeFunction> AttributeSetters;
};
FFileAttributesParserImpl::FFileAttributesParserImpl(IPlatformFile& InPlatformFile)
: PlatformFile(InPlatformFile)
{
AttributeSetters.Add(TEXT("readonly"), [](FFileAttributes& Attributes, FString Value){ Attributes.bReadOnly = true; });
AttributeSetters.Add(TEXT("compressed"), [](FFileAttributes& Attributes, FString Value){ Attributes.bCompressed = true; });
AttributeSetters.Add(TEXT("executable"), [](FFileAttributes& Attributes, FString Value){ Attributes.bUnixExecutable = true; });
AttributeSetters.Add(TEXT("tag"), [](FFileAttributes& Attributes, FString Value){ Attributes.InstallTags.Add(MoveTemp(Value)); });
}
FFileAttributesParserImpl::~FFileAttributesParserImpl()
{
}
bool FFileAttributesParserImpl::ParseFileAttributes(const FString& MetaFilename, TMap<FString, FFileAttributes>& FileAttributes)
{
TUniquePtr<IFileHandle> Handle(PlatformFile.OpenRead(*MetaFilename));
if (Handle)
{
TArray<uint8> FileData;
FileData.AddUninitialized(Handle->Size());
if (Handle->Read(FileData.GetData(), FileData.Num()))
{
FString FileDataString;
FFileHelper::BufferToString(FileDataString, FileData.GetData(), FileData.Num());
return FileAttributesMetaToMap(FileDataString, FileAttributes);
}
else
{
UE_LOG(LogFileAttributesParser, Error, TEXT("Could not read meta file %s"), *MetaFilename);
}
}
else
{
UE_LOG(LogFileAttributesParser, Error, TEXT("Could not open meta file %s"), *MetaFilename);
}
return false;
}
bool FFileAttributesParserImpl::FileAttributesMetaToMap(const FString& AttributesList, TMap<FString, FFileAttributes>& FileAttributesMap)
{
const TCHAR Quote = TEXT('\"');
const TCHAR EOFile = TEXT('\0');
const TCHAR EOLine = TEXT('\n');
bool Successful = true;
bool FoundFilename = false;
const TCHAR* CharPtr = *AttributesList;
while (*CharPtr != EOFile)
{
// Parse filename
while (*CharPtr != Quote && *CharPtr != EOFile){ ++CharPtr; }
if (*CharPtr == EOFile)
{
if (!FoundFilename)
{
UE_LOG(LogFileAttributesParser, Error, TEXT("Did not find opening quote for filename!"));
return false;
}
break;
}
const TCHAR* FilenameStart = ++CharPtr;
while (*CharPtr != Quote && *CharPtr != EOFile && *CharPtr != EOLine){ ++CharPtr; }
// Check we didn't run out of file
if (*CharPtr == EOFile)
{
UE_LOG(LogFileAttributesParser, Error, TEXT("Unexpected end of file before next quote! Pos:%" SIZE_T_FMT), CharPtr - *AttributesList);
return false;
}
// Check we didn't run out of line
if(*CharPtr == EOLine)
{
UE_LOG(LogFileAttributesParser, Error, TEXT("Unexpected end of line before next quote! Pos:%" SIZE_T_FMT), CharPtr - *AttributesList);
return false;
}
// Save positions
const TCHAR* FilenameEnd = CharPtr++;
const TCHAR* AttributesStart = CharPtr;
// Parse keywords
while (*CharPtr != Quote && *CharPtr != EOFile && *CharPtr != EOLine){ ++CharPtr; }
// Check we hit the end of the line or file, another quote it wrong
if (*CharPtr == Quote)
{
UE_LOG(LogFileAttributesParser, Error, TEXT("Unexpected Quote before end of keywords! Pos:%" SIZE_T_FMT), CharPtr - *AttributesList);
return false;
}
FoundFilename = true;
// Save position
const TCHAR* EndOfLine = CharPtr;
// Grab info
FString Filename = FString::ConstructFromPtrSize(FilenameStart, FilenameEnd - FilenameStart).Replace(TEXT("\\"), TEXT("/"));
FFileAttributes& FileAttributes = FileAttributesMap.FindOrAdd(Filename);
TArray<FString> AttributeParamsArray;
FString AttributeParams = FString::ConstructFromPtrSize(AttributesStart, EndOfLine - AttributesStart);
AttributeParams.ParseIntoArrayWS(AttributeParamsArray);
for (const FString& AttributeParam : AttributeParamsArray)
{
FString Key, Value;
if(!AttributeParam.Split(TEXT(":"), &Key, &Value))
{
Key = AttributeParam;
}
if (AttributeSetters.Contains(Key))
{
AttributeSetters[Key](FileAttributes, MoveTemp(Value));
}
else
{
UE_LOG(LogFileAttributesParser, Error, TEXT("Unrecognised attribute %s for %s"), *AttributeParam, *Filename);
Successful = false;
}
}
}
return Successful;
}
FFileAttributesParserRef FFileAttributesParserFactory::Create(IPlatformFile& PlatformFile)
{
return MakeShareable(new FFileAttributesParserImpl(PlatformFile));
}
}