October3d55/M/RuntimeAdd95a179eaf4V8/Source/RuntimeArchiver/Private/ArchiverTar/RuntimeArchiverTarHeader.cpp

310 lines
8.1 KiB
C++

// Georgy Treshchev 2024.
#include "ArchiverTar/RuntimeArchiverTarHeader.h"
#include "RuntimeArchiverDefines.h"
#include "RuntimeArchiverTypes.h"
#include "HAL/UnrealMemory.h"
#include "Containers/StringConv.h"
#include "ArchiverTar/RuntimeArchiverTarOperations.h"
/**
* Helper that handles type flags
*/
class FTarTypeFlagHelper
{
/** File type flags */
static const ANSICHAR FileTypeFlag;
static const ANSICHAR FileTypeFlag1;
/** Directory type flag */
static const ANSICHAR DirectoryTypeFlag;
/** Unsupported type flags */
static const ANSICHAR HardLinkTypeFlag;
static const ANSICHAR SymbolicLinkTypeFlag;
static const ANSICHAR CharacterDeviceTypeFlag;
static const ANSICHAR BlockDeviceTypeFlag;
static const ANSICHAR FIFOTypeFlag;
/** Array of string representation of type flags */
static const TMap<ANSICHAR, FString> Strings;
public:
/**
* Get a string representation of the specified type flag
*/
static FString ToString(ANSICHAR TypeFlag)
{
FString const* String = Strings.Find(TypeFlag);
if (!String)
{
return TEXT("Unrecognized type flag");
}
return *String;
}
/**
* Check if the specified type flag applies to a directory
*/
static bool IsDirectory(ANSICHAR TypeFlag)
{
if (TypeFlag == FileTypeFlag || TypeFlag == FileTypeFlag1)
{
return false;
}
if (TypeFlag == DirectoryTypeFlag)
{
return true;
}
UE_LOG(LogRuntimeArchiver, Error, TEXT("The type flag %c (%s) is not supported. Supported type flags are %c/%c (%s) and %c (%s)"),
TCHAR(TypeFlag), *ToString(TypeFlag),
TCHAR(FileTypeFlag), TCHAR(FileTypeFlag1), *ToString(FileTypeFlag),
TCHAR(DirectoryTypeFlag), *ToString(DirectoryTypeFlag));
return false;
}
/**
* Get type flag
*/
static ANSICHAR GetTypeFlag(bool bIsDirectory)
{
return bIsDirectory ? DirectoryTypeFlag : FileTypeFlag;
}
};
const ANSICHAR FTarTypeFlagHelper::FileTypeFlag{'0'};
const ANSICHAR FTarTypeFlagHelper::FileTypeFlag1{'\0'};
const ANSICHAR FTarTypeFlagHelper::DirectoryTypeFlag{'5'};
const ANSICHAR FTarTypeFlagHelper::HardLinkTypeFlag{'1'};
const ANSICHAR FTarTypeFlagHelper::SymbolicLinkTypeFlag{'2'};
const ANSICHAR FTarTypeFlagHelper::CharacterDeviceTypeFlag{'3'};
const ANSICHAR FTarTypeFlagHelper::BlockDeviceTypeFlag{'4'};
const ANSICHAR FTarTypeFlagHelper::FIFOTypeFlag{'6'};
const TMap<ANSICHAR, FString> FTarTypeFlagHelper::Strings{
{FileTypeFlag, TEXT("File")}, {FileTypeFlag1, TEXT("File")},
{DirectoryTypeFlag, TEXT("Directory")},
{HardLinkTypeFlag, TEXT("Hard link")},
{SymbolicLinkTypeFlag, TEXT("Symbolic link")},
{CharacterDeviceTypeFlag, TEXT("Character device")},
{BlockDeviceTypeFlag, TEXT("Block device")},
{FIFOTypeFlag, TEXT("FIFO")}
};
/**
* Helper that handles checksum
*/
class FTarChecksumHelper
{
public:
/**
* Check if the specified tar header has a valid checksum
*
* @param Header Header to check
* @return Whether the checksum is valid or not
*/
static bool IsValid(const FTarHeader& Header)
{
if (*Header.Checksum == '\0')
{
UE_LOG(LogRuntimeArchiver, Error, TEXT("Checksum in tar header '%s' is NULL"), StringCast<TCHAR>(Header.GetName()).Get());
return false;
}
if (BuildChecksum(Header) != Header.GetChecksum())
{
UE_LOG(LogRuntimeArchiver, Error, TEXT("Checksum in tar header %s' is invalid"), StringCast<TCHAR>(Header.GetName()).Get())
return false;
}
return true;
}
/**
* Build a checksum based primarily on tar header offsets
*
* @param Header Tar header based on which to build the checksum
* @return Built checksum
*/
static uint32 BuildChecksum(const FTarHeader& Header)
{
const uint8* HeaderPtr = reinterpret_cast<const uint8*>(&Header);
uint32 Checksum = 256;
for (size_t Index = 0; Index < STRUCT_OFFSET(FTarHeader, Checksum); ++Index)
{
Checksum += HeaderPtr[Index];
}
for (size_t Index = STRUCT_OFFSET(FTarHeader, TypeFlag); Index < sizeof(Header); ++Index)
{
Checksum += HeaderPtr[Index];
}
return Checksum;
}
};
FTarHeader::FTarHeader()
{
FMemory::Memset(this, 0, sizeof(FTarHeader));
}
bool FTarHeader::ToEntry(const FTarHeader& Header, int32 Index, FRuntimeArchiveEntry& Entry)
{
if (!FTarChecksumHelper::IsValid(Header))
{
return false;
}
Entry.Index = Index;
Entry.Name = StringCast<TCHAR>(Header.GetName()).Get();
Entry.bIsDirectory = FTarTypeFlagHelper::IsDirectory(Header.GetTypeFlag());
Entry.UncompressedSize = Header.GetSize();
Entry.CompressedSize = RuntimeArchiverTarOperations::RoundUp<int64>(Entry.UncompressedSize, 512);
Entry.CreationTime = FDateTime::FromUnixTimestamp(Header.GetTime());
return true;
}
bool FTarHeader::FromEntry(const FRuntimeArchiveEntry& Entry, FTarHeader& Header)
{
return GenerateHeader(Entry.Name, Entry.UncompressedSize, Entry.CreationTime, Entry.bIsDirectory, Header);
}
bool FTarHeader::GenerateHeader(const FString& Name, int64 Size, const FDateTime& CreationTime, bool bIsDirectory, FTarHeader& Header)
{
Header = FTarHeader();
if (!Header.SetName(StringCast<RA_UTF8CHAR>(*Name).Get()))
{
return false;
}
// Setting all possible permissions for Unix
Header.SetMode(0777);
Header.SetSize(Size);
Header.SetTime(CreationTime.ToUnixTimestamp());
Header.SetTypeFlag(FTarTypeFlagHelper::GetTypeFlag(bIsDirectory));
Header.SetChecksum(FTarChecksumHelper::BuildChecksum(Header));
return true;
}
const RA_UTF8CHAR* FTarHeader::GetName() const
{
return Name;
}
bool FTarHeader::SetName(const RA_UTF8CHAR* InName)
{
// Check if length is within bounds
if (TCString<RA_UTF8CHAR>::Strlen(InName) > UE_ARRAY_COUNT(Name))
{
UE_LOG(LogRuntimeArchiver, Error, TEXT("Name '%s' is too long for tar header. Maximum length is %d"), StringCast<TCHAR>(InName).Get(), static_cast<uint32>(UE_ARRAY_COUNT(Name)));
return false;
}
TCString<RA_UTF8CHAR>::Strncpy(Name, InName, UE_ARRAY_COUNT(Name));
return true;
}
uint32 FTarHeader::GetMode() const
{
return RuntimeArchiverTarOperations::OctalToDecimal<uint32>(Mode);
}
void FTarHeader::SetMode(uint32 InMode)
{
RuntimeArchiverTarOperations::DecimalToOctal<uint32>(InMode, Mode, UE_ARRAY_COUNT(Mode));
}
uint32 FTarHeader::GetOwner() const
{
return RuntimeArchiverTarOperations::OctalToDecimal<uint32>(Owner);
}
void FTarHeader::SetOwner(uint32 InOwner)
{
RuntimeArchiverTarOperations::DecimalToOctal<uint32>(InOwner, Owner, UE_ARRAY_COUNT(Owner));
}
const ANSICHAR* FTarHeader::GetGroup() const
{
return Group;
}
bool FTarHeader::SetGroup(const ANSICHAR* InGroup)
{
// Check if length is within bounds
if (FCStringAnsi::Strlen(InGroup) > UE_ARRAY_COUNT(Group))
{
UE_LOG(LogRuntimeArchiver, Error, TEXT("Group '%s' is too long for tar header. Maximum length is %d"), StringCast<TCHAR>(InGroup).Get(), static_cast<uint32>(UE_ARRAY_COUNT(Group)));
return false;
}
FCStringAnsi::Strncpy(Group, InGroup, UE_ARRAY_COUNT(Group));
return true;
}
int64 FTarHeader::GetSize() const
{
return RuntimeArchiverTarOperations::OctalToDecimal<int64>(Size);
}
void FTarHeader::SetSize(int64 InSize)
{
RuntimeArchiverTarOperations::DecimalToOctal<int64>(InSize, Size, UE_ARRAY_COUNT(Size));
}
int64 FTarHeader::GetTime() const
{
return RuntimeArchiverTarOperations::OctalToDecimal<int64>(Time);
}
void FTarHeader::SetTime(int64 InTime)
{
RuntimeArchiverTarOperations::DecimalToOctal<int64>(InTime, Time, UE_ARRAY_COUNT(Time));
}
uint32 FTarHeader::GetChecksum() const
{
return RuntimeArchiverTarOperations::OctalToDecimal<uint32>(Checksum);
}
void FTarHeader::SetChecksum(uint32 InChecksum)
{
RuntimeArchiverTarOperations::DecimalToOctal<uint32>(InChecksum, Checksum, UE_ARRAY_COUNT(Checksum));
}
ANSICHAR FTarHeader::GetTypeFlag() const
{
return TypeFlag;
}
void FTarHeader::SetTypeFlag(ANSICHAR InTypeFlag)
{
TypeFlag = InTypeFlag;
}
const ANSICHAR* FTarHeader::GetLinkName() const
{
return LinkName;
}
bool FTarHeader::SetLinkName(const ANSICHAR* InLinkName)
{
if (FCStringAnsi::Strlen(InLinkName) > UE_ARRAY_COUNT(LinkName))
{
UE_LOG(LogRuntimeArchiver, Error, TEXT("Link name '%s' is too long for tar header. Maximum length is %d"), StringCast<TCHAR>(InLinkName).Get(), static_cast<uint32>(UE_ARRAY_COUNT(LinkName)));
return false;
}
FCStringAnsi::Strncpy(LinkName, InLinkName, UE_ARRAY_COUNT(LinkName));
return true;
}