757 lines
27 KiB
C++
757 lines
27 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "PackageReader.h"
|
|
#include "HAL/FileManager.h"
|
|
#include "Internationalization/Internationalization.h"
|
|
#include "Logging/MessageLog.h"
|
|
#include "Misc/PackageName.h"
|
|
#include "UObject/Class.h"
|
|
#include "AssetRegistryPrivate.h"
|
|
#include "AssetRegistry/AssetData.h"
|
|
#include "AssetRegistry.h"
|
|
|
|
FPackageReader::FPackageReader()
|
|
: Loader(nullptr), PackageFileSize(0), AssetRegistryDependencyDataOffset(INDEX_NONE)
|
|
{
|
|
this->SetIsLoading(true);
|
|
this->SetIsPersistent(true);
|
|
}
|
|
|
|
FPackageReader::~FPackageReader()
|
|
{
|
|
if (Loader)
|
|
{
|
|
delete Loader;
|
|
}
|
|
}
|
|
|
|
bool FPackageReader::OpenPackageFile(const FString& InPackageFilename, EOpenPackageResult* OutErrorCode)
|
|
{
|
|
PackageFilename = InPackageFilename;
|
|
Loader = IFileManager::Get().CreateFileReader(*PackageFilename);
|
|
return OpenPackageFile(OutErrorCode);
|
|
}
|
|
|
|
bool FPackageReader::OpenPackageFile(FArchive* InLoader, EOpenPackageResult* OutErrorCode)
|
|
{
|
|
Loader = InLoader;
|
|
PackageFilename = Loader->GetArchiveName();
|
|
return OpenPackageFile(OutErrorCode);
|
|
}
|
|
|
|
bool FPackageReader::OpenPackageFile(EOpenPackageResult* OutErrorCode)
|
|
{
|
|
auto SetPackageErrorCode = [&](const EOpenPackageResult InErrorCode)
|
|
{
|
|
if (OutErrorCode)
|
|
{
|
|
*OutErrorCode = InErrorCode;
|
|
}
|
|
};
|
|
|
|
if (Loader == nullptr)
|
|
{
|
|
// Couldn't open the file
|
|
SetPackageErrorCode(EOpenPackageResult::NoLoader);
|
|
return false;
|
|
}
|
|
|
|
// Read package file summary from the file
|
|
*this << PackageFileSummary;
|
|
|
|
// Validate the summary.
|
|
|
|
// Make sure this is indeed a package
|
|
if (PackageFileSummary.Tag != PACKAGE_FILE_TAG || IsError())
|
|
{
|
|
// Unrecognized or malformed package file
|
|
UE_LOG(LogAssetRegistry, Error, TEXT("Package %s has malformed tag"), *PackageFilename);
|
|
SetPackageErrorCode(EOpenPackageResult::MalformedTag);
|
|
return false;
|
|
}
|
|
|
|
// Don't read packages that are too old
|
|
if (PackageFileSummary.GetFileVersionUE4() < VER_UE4_OLDEST_LOADABLE_PACKAGE)
|
|
{
|
|
UE_LOG(LogAssetRegistry, Error, TEXT("Package %s is too old"), *PackageFilename);
|
|
SetPackageErrorCode(EOpenPackageResult::VersionTooOld);
|
|
return false;
|
|
}
|
|
|
|
// Don't read packages that were saved with an package version newer than the current one.
|
|
if ((PackageFileSummary.GetFileVersionUE4() > GPackageFileUE4Version) || (PackageFileSummary.GetFileVersionLicenseeUE4() > GPackageFileLicenseeUE4Version))
|
|
{
|
|
UE_LOG(LogAssetRegistry, Error, TEXT("Package %s is too new"), *PackageFilename);
|
|
SetPackageErrorCode(EOpenPackageResult::VersionTooNew);
|
|
return false;
|
|
}
|
|
|
|
// Check serialized custom versions against latest custom versions.
|
|
TArray<FCustomVersionDifference> Diffs = FCurrentCustomVersions::Compare(PackageFileSummary.GetCustomVersionContainer().GetAllVersions(), *PackageFilename);
|
|
for (FCustomVersionDifference Diff: Diffs)
|
|
{
|
|
if (Diff.Type == ECustomVersionDifference::Missing)
|
|
{
|
|
SetPackageErrorCode(EOpenPackageResult::CustomVersionMissing);
|
|
return false;
|
|
}
|
|
else if (Diff.Type == ECustomVersionDifference::Invalid)
|
|
{
|
|
SetPackageErrorCode(EOpenPackageResult::CustomVersionInvalid);
|
|
return false;
|
|
}
|
|
else if (Diff.Type == ECustomVersionDifference::Newer)
|
|
{
|
|
UE_LOG(LogAssetRegistry, Error, TEXT("Package %s has newer custom version of %s"), *PackageFilename, *Diff.Version->GetFriendlyName().ToString());
|
|
|
|
SetPackageErrorCode(EOpenPackageResult::VersionTooNew);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// make sure the filereader gets the correct version number (it defaults to latest version)
|
|
SetUE4Ver(PackageFileSummary.GetFileVersionUE4());
|
|
SetLicenseeUE4Ver(PackageFileSummary.GetFileVersionLicenseeUE4());
|
|
SetEngineVer(PackageFileSummary.SavedByEngineVersion);
|
|
|
|
const FCustomVersionContainer& PackageFileSummaryVersions = PackageFileSummary.GetCustomVersionContainer();
|
|
SetCustomVersions(PackageFileSummaryVersions);
|
|
|
|
PackageFileSize = Loader->TotalSize();
|
|
|
|
SetPackageErrorCode(EOpenPackageResult::Success);
|
|
return true;
|
|
}
|
|
bool FPackageReader::StartSerializeSection(int64 Offset)
|
|
{
|
|
check(Loader);
|
|
if (Offset <= 0 || Offset > PackageFileSize)
|
|
{
|
|
return false;
|
|
}
|
|
ClearError();
|
|
Loader->ClearError();
|
|
Seek(Offset);
|
|
return !IsError();
|
|
}
|
|
|
|
#define UE_PACKAGEREADER_CORRUPTPACKAGE_WARNING(MessageKey, PackageFileName) \
|
|
do \
|
|
{\
|
|
FFormatNamedArguments CorruptPackageWarningArguments; \
|
|
CorruptPackageWarningArguments.Add(TEXT("FileName"), FText::FromString(PackageFileName)); \
|
|
FMessageLog("AssetRegistry").Warning(FText::Format(NSLOCTEXT("AssetRegistry", MessageKey, "Cannot read AssetRegistry Data in {FileName}, skipping it. Error: " MessageKey "."), CorruptPackageWarningArguments)); \
|
|
} while (false)
|
|
|
|
bool FPackageReader::ReadAssetRegistryData(TArray<FAssetData*>& AssetDataList)
|
|
{
|
|
if (!StartSerializeSection(PackageFileSummary.AssetRegistryDataOffset))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Determine the package name and path
|
|
FString PackageName;
|
|
if (!FPackageName::TryConvertFilenameToLongPackageName(PackageFilename, PackageName))
|
|
{
|
|
// Path was possibly unmounted
|
|
return false;
|
|
}
|
|
|
|
using namespace UE::AssetRegistry;
|
|
|
|
EReadPackageDataMainErrorCode ErrorCode;
|
|
if (!ReadPackageDataMain(*this, PackageName, PackageFileSummary, AssetRegistryDependencyDataOffset, AssetDataList, ErrorCode))
|
|
{
|
|
switch (ErrorCode)
|
|
{
|
|
case EReadPackageDataMainErrorCode::InvalidObjectCount:
|
|
UE_PACKAGEREADER_CORRUPTPACKAGE_WARNING("EReadPackageDataMainErrorCode::InvalidObjectCount", PackageFilename);
|
|
break;
|
|
case EReadPackageDataMainErrorCode::InvalidTagCount:
|
|
UE_PACKAGEREADER_CORRUPTPACKAGE_WARNING("EReadPackageDataMainErrorCode::InvalidTagCount", PackageFilename);
|
|
break;
|
|
case EReadPackageDataMainErrorCode::InvalidTag:
|
|
UE_PACKAGEREADER_CORRUPTPACKAGE_WARNING("EReadPackageDataMainErrorCode::InvalidTag", PackageFilename);
|
|
break;
|
|
default:
|
|
UE_PACKAGEREADER_CORRUPTPACKAGE_WARNING("EReadPackageDataMainErrorCode::Unknown", PackageFilename);
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool FPackageReader::SerializeAssetRegistryDependencyData(FPackageDependencyData& DependencyData)
|
|
{
|
|
if (AssetRegistryDependencyDataOffset == INDEX_NONE)
|
|
{
|
|
// For old package versions that did not write out the dependency flags, set default values of the flags
|
|
DependencyData.ImportUsedInGame.Init(true, DependencyData.ImportMap.Num());
|
|
DependencyData.SoftPackageUsedInGame.Init(true, DependencyData.SoftPackageReferenceList.Num());
|
|
return true;
|
|
}
|
|
|
|
if (!StartSerializeSection(AssetRegistryDependencyDataOffset))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!UE::AssetRegistry::ReadPackageDataDependencies(*this, DependencyData.ImportUsedInGame, DependencyData.SoftPackageUsedInGame) || !DependencyData.IsValid())
|
|
{
|
|
UE_PACKAGEREADER_CORRUPTPACKAGE_WARNING("SerializeAssetRegistryDependencyData", PackageFilename);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool FPackageReader::ReadAssetDataFromThumbnailCache(TArray<FAssetData*>& AssetDataList)
|
|
{
|
|
if (!StartSerializeSection(PackageFileSummary.ThumbnailTableOffset))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Determine the package name and path
|
|
FString PackageName = FPackageName::FilenameToLongPackageName(PackageFilename);
|
|
FString PackagePath = FPackageName::GetLongPackagePath(PackageName);
|
|
|
|
// Load the thumbnail count
|
|
int32 ObjectCount = 0;
|
|
*this << ObjectCount;
|
|
const int32 MinBytesPerObject = 1;
|
|
if (IsError() || ObjectCount < 0 || PackageFileSize < Tell() + ObjectCount * MinBytesPerObject)
|
|
{
|
|
UE_PACKAGEREADER_CORRUPTPACKAGE_WARNING("ReadAssetDataFromThumbnailCacheInvalidObjectCount", PackageFilename);
|
|
return false;
|
|
}
|
|
|
|
// Iterate over every thumbnail entry and harvest the objects classnames
|
|
for (int32 ObjectIdx = 0; ObjectIdx < ObjectCount; ++ObjectIdx)
|
|
{
|
|
// Serialize the classname
|
|
FString AssetClassName;
|
|
*this << AssetClassName;
|
|
|
|
// Serialize the object path.
|
|
FString ObjectPathWithoutPackageName;
|
|
*this << ObjectPathWithoutPackageName;
|
|
|
|
// Serialize the rest of the data to get at the next object
|
|
int32 FileOffset = 0;
|
|
*this << FileOffset;
|
|
|
|
if (IsError())
|
|
{
|
|
UE_PACKAGEREADER_CORRUPTPACKAGE_WARNING("ReadAssetDataFromThumbnailCacheInvalidObject", PackageFilename);
|
|
return false;
|
|
}
|
|
|
|
FString GroupNames;
|
|
FString AssetName;
|
|
|
|
if (!ensureMsgf(!ObjectPathWithoutPackageName.Contains(TEXT("."), ESearchCase::CaseSensitive), TEXT("Cannot make FAssetData for sub object %s!"), *ObjectPathWithoutPackageName))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Create a new FAssetData for this asset and update it with the gathered data
|
|
AssetDataList.Add(new FAssetData(FName(*PackageName), FName(*PackagePath), FName(*ObjectPathWithoutPackageName), FName(*AssetClassName), FAssetDataTagMap(), PackageFileSummary.ChunkIDs, PackageFileSummary.PackageFlags));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool FPackageReader::ReadAssetRegistryDataIfCookedPackage(TArray<FAssetData*>& AssetDataList, TArray<FString>& CookedPackageNamesWithoutAssetData)
|
|
{
|
|
if (!!(GetPackageFlags() & PKG_FilterEditorOnly))
|
|
{
|
|
const FString PackageName = FPackageName::FilenameToLongPackageName(PackageFilename);
|
|
|
|
bool bFoundAtLeastOneAsset = false;
|
|
|
|
// If the packaged is saved with the right version we have the information
|
|
// which of the objects in the export map as the asset.
|
|
// Otherwise we need to store a temp minimal data and then force load the asset
|
|
// to re-generate its registry data
|
|
if (UE4Ver() >= VER_UE4_COOKED_ASSETS_IN_EDITOR_SUPPORT)
|
|
{
|
|
const FString PackagePath = FPackageName::GetLongPackagePath(PackageName);
|
|
|
|
TArray<FObjectImport> ImportMap;
|
|
TArray<FObjectExport> ExportMap;
|
|
if (!SerializeNameMap())
|
|
{
|
|
return false;
|
|
}
|
|
if (!SerializeImportMap(ImportMap))
|
|
{
|
|
return false;
|
|
}
|
|
if (!SerializeExportMap(ExportMap))
|
|
{
|
|
return false;
|
|
}
|
|
for (FObjectExport& Export: ExportMap)
|
|
{
|
|
if (Export.bIsAsset)
|
|
{
|
|
// We need to get the class name from the import/export maps
|
|
FName ObjectClassName;
|
|
if (Export.ClassIndex.IsNull())
|
|
{
|
|
ObjectClassName = UClass::StaticClass()->GetFName();
|
|
}
|
|
else if (Export.ClassIndex.IsExport())
|
|
{
|
|
const FObjectExport& ClassExport = ExportMap[Export.ClassIndex.ToExport()];
|
|
ObjectClassName = ClassExport.ObjectName;
|
|
}
|
|
else if (Export.ClassIndex.IsImport())
|
|
{
|
|
const FObjectImport& ClassImport = ImportMap[Export.ClassIndex.ToImport()];
|
|
ObjectClassName = ClassImport.ObjectName;
|
|
}
|
|
|
|
AssetDataList.Add(new FAssetData(FName(*PackageName), FName(*PackagePath), Export.ObjectName, ObjectClassName, FAssetDataTagMap(), TArray<int32>(), GetPackageFlags()));
|
|
bFoundAtLeastOneAsset = true;
|
|
}
|
|
}
|
|
}
|
|
if (!bFoundAtLeastOneAsset)
|
|
{
|
|
CookedPackageNamesWithoutAssetData.Add(PackageName);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool FPackageReader::ReadDependencyData(FPackageDependencyData& OutDependencyData)
|
|
{
|
|
FString PackageNameString;
|
|
if (!FPackageName::TryConvertFilenameToLongPackageName(PackageFilename, PackageNameString))
|
|
{
|
|
// Path was possibly unmounted
|
|
return false;
|
|
}
|
|
|
|
OutDependencyData.PackageName = FName(*PackageNameString);
|
|
OutDependencyData.PackageData.DiskSize = PackageFileSize;
|
|
PRAGMA_DISABLE_DEPRECATION_WARNINGS
|
|
OutDependencyData.PackageData.PackageGuid = PackageFileSummary.Guid;
|
|
PRAGMA_ENABLE_DEPRECATION_WARNINGS
|
|
|
|
if (!SerializeNameMap())
|
|
{
|
|
return false;
|
|
}
|
|
if (!SerializeImportMap(OutDependencyData.ImportMap))
|
|
{
|
|
return false;
|
|
}
|
|
if (!SerializeSoftPackageReferenceList(OutDependencyData.SoftPackageReferenceList))
|
|
{
|
|
return false;
|
|
}
|
|
if (!SerializeSearchableNamesMap(OutDependencyData))
|
|
{
|
|
return false;
|
|
}
|
|
if (!SerializeAssetRegistryDependencyData(OutDependencyData))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
checkf(OutDependencyData.IsValid(), TEXT("We should have early exited above rather than creating invalid dependency data"));
|
|
return true;
|
|
}
|
|
|
|
bool FPackageReader::SerializeNameMap()
|
|
{
|
|
if (PackageFileSummary.NameCount > 0)
|
|
{
|
|
if (!StartSerializeSection(PackageFileSummary.NameOffset))
|
|
{
|
|
UE_PACKAGEREADER_CORRUPTPACKAGE_WARNING("SerializeNameMapInvalidNameOffset", PackageFilename);
|
|
return false;
|
|
}
|
|
|
|
const int MinSizePerNameEntry = 1;
|
|
if (PackageFileSize < Tell() + PackageFileSummary.NameCount * MinSizePerNameEntry)
|
|
{
|
|
UE_PACKAGEREADER_CORRUPTPACKAGE_WARNING("SerializeNameMapInvalidNameCount", PackageFilename);
|
|
return false;
|
|
}
|
|
|
|
for (int32 NameMapIdx = 0; NameMapIdx < PackageFileSummary.NameCount; ++NameMapIdx)
|
|
{
|
|
// Read the name entry from the file.
|
|
FNameEntrySerialized NameEntry(ENAME_LinkerConstructor);
|
|
*this << NameEntry;
|
|
if (IsError())
|
|
{
|
|
UE_PACKAGEREADER_CORRUPTPACKAGE_WARNING("SerializeNameMapInvalidName", PackageFilename);
|
|
return false;
|
|
}
|
|
NameMap.Add(FName(NameEntry));
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool FPackageReader::SerializeImportMap(TArray<FObjectImport>& OutImportMap)
|
|
{
|
|
if (PackageFileSummary.ImportCount > 0)
|
|
{
|
|
if (!StartSerializeSection(PackageFileSummary.ImportOffset))
|
|
{
|
|
UE_PACKAGEREADER_CORRUPTPACKAGE_WARNING("SerializeImportMapInvalidImportOffset", PackageFilename);
|
|
return false;
|
|
}
|
|
|
|
const int MinSizePerImport = 1;
|
|
if (PackageFileSize < Tell() + PackageFileSummary.ImportCount * MinSizePerImport)
|
|
{
|
|
UE_PACKAGEREADER_CORRUPTPACKAGE_WARNING("SerializeImportMapInvalidImportCount", PackageFilename);
|
|
return false;
|
|
}
|
|
for (int32 ImportMapIdx = 0; ImportMapIdx < PackageFileSummary.ImportCount; ++ImportMapIdx)
|
|
{
|
|
FObjectImport* Import = new (OutImportMap) FObjectImport;
|
|
*this << *Import;
|
|
if (IsError())
|
|
{
|
|
UE_PACKAGEREADER_CORRUPTPACKAGE_WARNING("SerializeImportMapInvalidImport", PackageFilename);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool FPackageReader::SerializeExportMap(TArray<FObjectExport>& OutExportMap)
|
|
{
|
|
if (PackageFileSummary.ExportCount > 0)
|
|
{
|
|
if (!StartSerializeSection(PackageFileSummary.ExportOffset))
|
|
{
|
|
UE_PACKAGEREADER_CORRUPTPACKAGE_WARNING("SerializeExportMapInvalidExportOffset", PackageFilename);
|
|
return false;
|
|
}
|
|
|
|
const int MinSizePerExport = 1;
|
|
if (PackageFileSize < Tell() + PackageFileSummary.ExportCount * MinSizePerExport)
|
|
{
|
|
UE_PACKAGEREADER_CORRUPTPACKAGE_WARNING("SerializeExportMapInvalidExportCount", PackageFilename);
|
|
return false;
|
|
}
|
|
for (int32 ExportMapIdx = 0; ExportMapIdx < PackageFileSummary.ExportCount; ++ExportMapIdx)
|
|
{
|
|
FObjectExport* Export = new (OutExportMap) FObjectExport;
|
|
*this << *Export;
|
|
if (IsError())
|
|
{
|
|
UE_PACKAGEREADER_CORRUPTPACKAGE_WARNING("SerializeExportMapInvalidExport", PackageFilename);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool FPackageReader::SerializeSoftPackageReferenceList(TArray<FName>& OutSoftPackageReferenceList)
|
|
{
|
|
if (UE4Ver() >= VER_UE4_ADD_STRING_ASSET_REFERENCES_MAP && PackageFileSummary.SoftPackageReferencesOffset > 0 && PackageFileSummary.SoftPackageReferencesCount > 0)
|
|
{
|
|
if (!StartSerializeSection(PackageFileSummary.SoftPackageReferencesOffset))
|
|
{
|
|
UE_PACKAGEREADER_CORRUPTPACKAGE_WARNING("SerializeSoftPackageReferenceListInvalidReferencesOffset", PackageFilename);
|
|
return false;
|
|
}
|
|
|
|
const int MinSizePerSoftPackageReference = 1;
|
|
if (PackageFileSize < Tell() + PackageFileSummary.SoftPackageReferencesCount * MinSizePerSoftPackageReference)
|
|
{
|
|
UE_PACKAGEREADER_CORRUPTPACKAGE_WARNING("SerializeSoftPackageReferenceListInvalidReferencesCount", PackageFilename);
|
|
return false;
|
|
}
|
|
if (UE4Ver() < VER_UE4_ADDED_SOFT_OBJECT_PATH)
|
|
{
|
|
for (int32 ReferenceIdx = 0; ReferenceIdx < PackageFileSummary.SoftPackageReferencesCount; ++ReferenceIdx)
|
|
{
|
|
FString PackageName;
|
|
*this << PackageName;
|
|
if (IsError())
|
|
{
|
|
UE_PACKAGEREADER_CORRUPTPACKAGE_WARNING("SerializeSoftPackageReferenceListInvalidReferencePreSoftObjectPath", PackageFilename);
|
|
return false;
|
|
}
|
|
|
|
if (UE4Ver() < VER_UE4_KEEP_ONLY_PACKAGE_NAMES_IN_STRING_ASSET_REFERENCES_MAP)
|
|
{
|
|
PackageName = FPackageName::GetNormalizedObjectPath(PackageName);
|
|
if (!PackageName.IsEmpty())
|
|
{
|
|
PackageName = FPackageName::ObjectPathToPackageName(PackageName);
|
|
}
|
|
}
|
|
|
|
OutSoftPackageReferenceList.Add(FName(*PackageName));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int32 ReferenceIdx = 0; ReferenceIdx < PackageFileSummary.SoftPackageReferencesCount; ++ReferenceIdx)
|
|
{
|
|
FName PackageName;
|
|
*this << PackageName;
|
|
if (IsError())
|
|
{
|
|
UE_PACKAGEREADER_CORRUPTPACKAGE_WARNING("SerializeSoftPackageReferenceListInvalidReference", PackageFilename);
|
|
return false;
|
|
}
|
|
|
|
OutSoftPackageReferenceList.Add(PackageName);
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool FPackageReader::SerializeSearchableNamesMap(FPackageDependencyData& OutDependencyData)
|
|
{
|
|
if (UE4Ver() >= VER_UE4_ADDED_SEARCHABLE_NAMES && PackageFileSummary.SearchableNamesOffset > 0)
|
|
{
|
|
if (!StartSerializeSection(PackageFileSummary.SearchableNamesOffset))
|
|
{
|
|
UE_PACKAGEREADER_CORRUPTPACKAGE_WARNING("SerializeSearchableNamesMapInvalidOffset", PackageFilename);
|
|
return false;
|
|
}
|
|
|
|
OutDependencyData.SerializeSearchableNamesMap(*this);
|
|
if (IsError())
|
|
{
|
|
UE_PACKAGEREADER_CORRUPTPACKAGE_WARNING("SerializeSearchableNamesMapInvalidSearchableNamesMap", PackageFilename);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void FPackageReader::Serialize(void* V, int64 Length)
|
|
{
|
|
check(Loader);
|
|
Loader->Serialize(V, Length);
|
|
if (Loader->IsError())
|
|
{
|
|
SetError();
|
|
}
|
|
}
|
|
|
|
bool FPackageReader::Precache(int64 PrecacheOffset, int64 PrecacheSize)
|
|
{
|
|
check(Loader);
|
|
return Loader->Precache(PrecacheOffset, PrecacheSize);
|
|
}
|
|
|
|
void FPackageReader::Seek(int64 InPos)
|
|
{
|
|
check(Loader);
|
|
Loader->Seek(InPos);
|
|
if (Loader->IsError())
|
|
{
|
|
SetError();
|
|
}
|
|
}
|
|
|
|
int64 FPackageReader::Tell()
|
|
{
|
|
check(Loader);
|
|
return Loader->Tell();
|
|
}
|
|
|
|
int64 FPackageReader::TotalSize()
|
|
{
|
|
check(Loader);
|
|
return Loader->TotalSize();
|
|
}
|
|
|
|
uint32 FPackageReader::GetPackageFlags() const
|
|
{
|
|
return PackageFileSummary.PackageFlags;
|
|
}
|
|
|
|
FArchive& FPackageReader::operator<<(FName& Name)
|
|
{
|
|
int32 NameIndex;
|
|
FArchive& Ar = *this;
|
|
Ar << NameIndex;
|
|
|
|
if (!NameMap.IsValidIndex(NameIndex))
|
|
{
|
|
UE_LOG(LogAssetRegistry, Warning, TEXT("Bad name index %i/%i when reading package %s"), NameIndex, NameMap.Num(), *PackageFilename);
|
|
SetError();
|
|
return *this;
|
|
}
|
|
|
|
// if the name wasn't loaded (because it wasn't valid in this context)
|
|
if (NameMap[NameIndex] == NAME_None)
|
|
{
|
|
int32 TempNumber;
|
|
Ar << TempNumber;
|
|
Name = NAME_None;
|
|
}
|
|
else
|
|
{
|
|
int32 Number;
|
|
Ar << Number;
|
|
// simply create the name from the NameMap's name and the serialized instance number
|
|
Name = FName(NameMap[NameIndex], Number);
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
namespace UE
|
|
{
|
|
namespace AssetRegistry
|
|
{
|
|
// See the corresponding WritePackageData defined in SavePackageUtilities.cpp in CoreUObject module
|
|
bool ReadPackageDataMain(FArchive& BinaryArchive, const FString& PackageName, const FPackageFileSummary& PackageFileSummary, int64& OutDependencyDataOffset, TArray<FAssetData*>& OutAssetDataList, EReadPackageDataMainErrorCode& OutError)
|
|
{
|
|
OutError = EReadPackageDataMainErrorCode::Unknown;
|
|
|
|
const FString PackagePath = FPackageName::GetLongPackagePath(PackageName);
|
|
const int64 PackageFileSize = BinaryArchive.TotalSize();
|
|
const bool bIsMapPackage = (PackageFileSummary.PackageFlags & PKG_ContainsMap) != 0;
|
|
|
|
// To avoid large patch sizes, we have frozen cooked package format at the format before VER_UE4_ASSETREGISTRY_DEPENDENCYFLAGS
|
|
bool bPreDependencyFormat = PackageFileSummary.GetFileVersionUE4() < VER_UE4_ASSETREGISTRY_DEPENDENCYFLAGS || !!(PackageFileSummary.PackageFlags & PKG_FilterEditorOnly);
|
|
|
|
// Load offsets to optionally-read data
|
|
if (bPreDependencyFormat)
|
|
{
|
|
OutDependencyDataOffset = INDEX_NONE;
|
|
}
|
|
else
|
|
{
|
|
BinaryArchive << OutDependencyDataOffset;
|
|
}
|
|
|
|
// Load the object count
|
|
int32 ObjectCount = 0;
|
|
BinaryArchive << ObjectCount;
|
|
const int32 MinBytesPerObject = 1;
|
|
if (BinaryArchive.IsError() || ObjectCount < 0 || PackageFileSize < BinaryArchive.Tell() + ObjectCount * MinBytesPerObject)
|
|
{
|
|
OutError = EReadPackageDataMainErrorCode::InvalidObjectCount;
|
|
return false;
|
|
}
|
|
|
|
// Worlds that were saved before they were marked public do not have asset data so we will synthesize it here to make sure we see all legacy umaps
|
|
// We will also do this for maps saved after they were marked public but no asset data was saved for some reason. A bug caused this to happen for some maps.
|
|
if (bIsMapPackage)
|
|
{
|
|
const bool bLegacyPackage = PackageFileSummary.GetFileVersionUE4() < VER_UE4_PUBLIC_WORLDS;
|
|
const bool bNoMapAsset = (ObjectCount == 0);
|
|
if (bLegacyPackage || bNoMapAsset)
|
|
{
|
|
FString AssetName = FPackageName::GetLongPackageAssetName(PackageName);
|
|
OutAssetDataList.Add(new FAssetData(FName(*PackageName), FName(*PackagePath), FName(*AssetName), FName(TEXT("World")), FAssetDataTagMap(), PackageFileSummary.ChunkIDs, PackageFileSummary.PackageFlags));
|
|
}
|
|
}
|
|
|
|
const int32 MinBytesPerTag = 1;
|
|
// UAsset files usually only have one asset, maps and redirectors have multiple
|
|
for (int32 ObjectIdx = 0; ObjectIdx < ObjectCount; ++ObjectIdx)
|
|
{
|
|
FString ObjectPath;
|
|
FString ObjectClassName;
|
|
int32 TagCount = 0;
|
|
BinaryArchive << ObjectPath;
|
|
BinaryArchive << ObjectClassName;
|
|
BinaryArchive << TagCount;
|
|
if (BinaryArchive.IsError() || TagCount < 0 || PackageFileSize < BinaryArchive.Tell() + TagCount * MinBytesPerTag)
|
|
{
|
|
OutError = EReadPackageDataMainErrorCode::InvalidTagCount;
|
|
return false;
|
|
}
|
|
|
|
FAssetDataTagMap TagsAndValues;
|
|
TagsAndValues.Reserve(TagCount);
|
|
|
|
for (int32 TagIdx = 0; TagIdx < TagCount; ++TagIdx)
|
|
{
|
|
FString Key;
|
|
FString Value;
|
|
BinaryArchive << Key;
|
|
BinaryArchive << Value;
|
|
if (BinaryArchive.IsError())
|
|
{
|
|
OutError = EReadPackageDataMainErrorCode::InvalidTag;
|
|
return false;
|
|
}
|
|
|
|
if (!Key.IsEmpty() && !Value.IsEmpty())
|
|
{
|
|
TagsAndValues.Add(FName(*Key), Value);
|
|
}
|
|
}
|
|
|
|
// Before worlds were RF_Public, other non-public assets were added to the asset data table in map packages.
|
|
// Here we simply skip over them
|
|
if (bIsMapPackage && PackageFileSummary.GetFileVersionUE4() < VER_UE4_PUBLIC_WORLDS)
|
|
{
|
|
if (ObjectPath != FPackageName::GetLongPackageAssetName(PackageName))
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// if we have an object path that starts with the package then this asset is outer-ed to another package
|
|
const bool bFullObjectPath = ObjectPath.StartsWith(TEXT("/"), ESearchCase::CaseSensitive);
|
|
|
|
// if we do not have a full object path already, build it
|
|
if (!bFullObjectPath)
|
|
{
|
|
// if we do not have a full object path, ensure that we have a top level object for the package and not a sub object
|
|
if (!ensureMsgf(!ObjectPath.Contains(TEXT("."), ESearchCase::CaseSensitive), TEXT("Cannot make FAssetData for sub object %s in package %s!"), *ObjectPath, *PackageName))
|
|
{
|
|
UE_ASSET_LOG(LogAssetRegistry, Warning, *PackageName, TEXT("Cannot make FAssetData for sub object %s!"), *ObjectPath);
|
|
continue;
|
|
}
|
|
ObjectPath = PackageName + TEXT(".") + ObjectPath;
|
|
}
|
|
// Previously export couldn't have its outer as an import
|
|
else if (PackageFileSummary.GetFileVersionUE4() < VER_UE4_NON_OUTER_PACKAGE_IMPORT)
|
|
{
|
|
UE_ASSET_LOG(LogAssetRegistry, Warning, *PackageName, TEXT("Package has invalid export %s, resave source package!"), *ObjectPath);
|
|
continue;
|
|
}
|
|
|
|
// Create a new FAssetData for this asset and update it with the gathered data
|
|
OutAssetDataList.Add(new FAssetData(PackageName, ObjectPath, FName(*ObjectClassName), MoveTemp(TagsAndValues), PackageFileSummary.ChunkIDs, PackageFileSummary.PackageFlags));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// See the corresponding WriteAssetRegistryPackageData defined in SavePackageUtilities.cpp in CoreUObject module
|
|
bool ReadPackageDataDependencies(FArchive& BinaryArchive, TBitArray<>& OutImportUsedInGame, TBitArray<>& OutSoftPackageUsedInGame)
|
|
{
|
|
BinaryArchive << OutImportUsedInGame;
|
|
BinaryArchive << OutSoftPackageUsedInGame;
|
|
return !BinaryArchive.IsError();
|
|
}
|
|
} // namespace AssetRegistry
|
|
} // namespace UE
|