首先是读取文件头:
文件头结构体:
Engine_Updating\Engine\Source\Runtime\CoreUObject\Public\UObject\Linker.h
/**
* A "table of contents" for an Unreal package file. Stored at the top of the file.
*/
struct FPackageFileSummary
{
/**
* Magic tag compared against PACKAGE_FILE_TAG to ensure that package is an Unreal package.
*/
int32 Tag;
private:
/* UE4 file version */
int32 FileVersionUE4;
/* Licensee file version */
int32 FileVersionLicenseeUE4;
/* Custom version numbers. Keyed off a unique tag for each custom component. */
FCustomVersionContainer CustomVersionContainer;
public:
/**
* Total size of all information that needs to be read in to create a FLinkerLoad. This includes
* the package file summary, name table and import & export maps.
*/
int32 TotalHeaderSize;
/**
* The flags for the package
*/
uint32 PackageFlags;
/**
* The Generic Browser folder name that this package lives in
*/
FString FolderName;
/**
* Number of names used in this package
*/
int32 NameCount;
/**
* Location into the file on disk for the name data
*/
int32 NameOffset;
/**
* Number of gatherable text data items in this package
*/
int32 GatherableTextDataCount;
/**
* Location into the file on disk for the gatherable text data items
*/
int32 GatherableTextDataOffset;
/**
* Number of exports contained in this package
*/
int32 ExportCount;
/**
* Location into the file on disk for the ExportMap data
*/
int32 ExportOffset;
/**
* Number of imports contained in this package
*/
int32 ImportCount;
/**
* Location into the file on disk for the ImportMap data
*/
int32 ImportOffset;
/**
* Location into the file on disk for the DependsMap data
*/
int32 DependsOffset;
/**
* Number of references contained in this package
*/
int32 StringAssetReferencesCount;
/**
* Location into the file on disk for the string asset references map data
*/
int32 StringAssetReferencesOffset;
/**
* Thumbnail table offset
*/
int32 ThumbnailTableOffset;
/**
* Current id for this package
*/
FGuid Guid;
/**
* Data about previous versions of this package
*/
TArray Generations;
/**
* Engine version this package was saved with. For hotfix releases and engine versions which maintain strict binary compatibility with another version, this may differ from CompatibleWithEngineVersion.
*/
FEngineVersion SavedByEngineVersion;
/**
* Engine version this package is compatible with. See SavedByEngineVersion.
*/
FEngineVersion CompatibleWithEngineVersion;
/**
* Flags used to compress the file on save and uncompress on load.
*/
uint32 CompressionFlags;
/**
* Value that is used to determine if the package was saved by Epic (or licensee) or by a modder, etc
*/
uint32 PackageSource;
/**
* Array of compressed chunks in case this package was stored compressed.
*/
TArray CompressedChunks;
/**
* List of additional packages that are needed to be cooked for this package (ie streaming levels)
*/
TArray AdditionalPackagesToCook;
/**
* If true, this file will not be saved with version numbers or was saved without version numbers. In this case they are assumed to be the current version.
* This is only used for full cooks for distribution because it is hard to guarantee correctness
**/
bool bUnversioned;
#if WITH_ENGINE
/**
* Information about the textures stored in the package.
*/
FTextureAllocations TextureAllocations;
#endif // WITH_ENGINE
/**
* Location into the file on disk for the asset registry tag data
*/
int32 AssetRegistryDataOffset;
/** Offset to the location in the file where the bulkdata starts */
int64 BulkDataStartOffset;
/**
* Offset to the location in the file where the FWorldTileInfo data starts
*/
int32 WorldTileInfoDataOffset;
/**
* Streaming install ChunkIDs
*/
TArray ChunkIDs;
/** Constructor */
COREUOBJECT_API FPackageFileSummary();
int32 GetFileVersionUE4() const
{
return FileVersionUE4;
}
int32 GetFileVersionLicenseeUE4() const
{
return FileVersionLicenseeUE4;
}
const FCustomVersionContainer& GetCustomVersionContainer() const
{
return CustomVersionContainer;
}
void SetCustomVersionContainer(const FCustomVersionContainer& InContainer)
{
CustomVersionContainer = InContainer;
}
void SetFileVersions(const int32 EpicUE4, const int32 LicenseeUE4, const bool bInSaveUnversioned = false)
{
FileVersionUE4 = EpicUE4;
FileVersionLicenseeUE4 = LicenseeUE4;
bUnversioned = bInSaveUnversioned;
}
/** I/O function */
friend COREUOBJECT_API FArchive& operator<<( FArchive& Ar, FPackageFileSummary& Sum );
};
读取文件头函数位置:
Engine_Updating\Engine\Source\Runtime\CoreUObject\Private\UObject\PackageFileSummary.cpp
FArchive& operator<<( FArchive& Ar, FPackageFileSummary& Sum )
{
bool bCanStartSerializing = true;
int64 ArchiveSize = 0;
if (Ar.IsLoading())
{
// Sanity checks before we even start serializing the archive
ArchiveSize = Ar.TotalSize();
const int64 MinimumPackageSize = 32; // That should get us safely to Sum.TotalHeaderSize
bCanStartSerializing = ArchiveSize >= MinimumPackageSize;
UE_CLOG(!bCanStartSerializing, LogLinker, Warning,
TEXT("Failed to read package file summary, the file \"%s\" is too small (%lld bytes, expected at least %lld bytes)"),
*Ar.GetArchiveName(), ArchiveSize, MinimumPackageSize);
}
if (bCanStartSerializing)
{
Ar << Sum.Tag;
}
// only keep loading if we match the magic
if( Sum.Tag == PACKAGE_FILE_TAG || Sum.Tag == PACKAGE_FILE_TAG_SWAPPED )
{
// The package has been stored in a separate endianness than the linker expected so we need to force
// endian conversion. Latent handling allows the PC version to retrieve information about cooked packages.
if( Sum.Tag == PACKAGE_FILE_TAG_SWAPPED )
{
// Set proper tag.
Sum.Tag = PACKAGE_FILE_TAG;
// Toggle forced byte swapping.
if( Ar.ForceByteSwapping() )
{
Ar.SetByteSwapping( false );
}
else
{
Ar.SetByteSwapping( true );
}
}
/**
* The package file version number when this package was saved.
*
* Lower 16 bits stores the UE3 engine version
* Upper 16 bits stores the UE4/licensee version
* For newer packages this is -6
* -2 indicates presence of enum-based custom versions
* -3 indicates guid-based custom versions
* -4 indicates removal of the UE3 version. Packages saved with this ID cannot be loaded in older engine versions
* -5 indicates the replacement of writing out the "UE3 version" so older versions of engine can gracefully fail to open newer packages
* -6 indicates optimizations to how custom versions are being serialized
*/
const int32 CurrentLegacyFileVersion = -6;
int32 LegacyFileVersion = CurrentLegacyFileVersion;
Ar << LegacyFileVersion;
if (Ar.IsLoading())
{
if (LegacyFileVersion < 0) // means we have modern version numbers
{
if (LegacyFileVersion < CurrentLegacyFileVersion)
{
// we can't safely load more than this because the legacy version code differs in ways we can not predict.
// Make sure that the linker will fail to load with it.
Sum.FileVersionUE4 = 0;
Sum.FileVersionLicenseeUE4 = 0;
return Ar;
}
if (LegacyFileVersion != -4)
{
int32 LegacyUE3Version = 0;
Ar << LegacyUE3Version;
}
Ar << Sum.FileVersionUE4;
Ar << Sum.FileVersionLicenseeUE4;
if (LegacyFileVersion <= -2)
{
Sum.CustomVersionContainer.Serialize(Ar, GetCustomVersionFormatForArchive(LegacyFileVersion));
}
if (!Sum.FileVersionUE4 && !Sum.FileVersionLicenseeUE4)
{
// this file is unversioned, remember that, then use current versions
Sum.bUnversioned = true;
Sum.FileVersionUE4 = GPackageFileUE4Version;
Sum.FileVersionLicenseeUE4 = GPackageFileLicenseeUE4Version;
Sum.CustomVersionContainer = FCustomVersionContainer::GetRegistered();
}
}
else
{
// This is probably an old UE3 file, make sure that the linker will fail to load with it.
Sum.FileVersionUE4 = 0;
Sum.FileVersionLicenseeUE4 = 0;
}
}
else
{
if (Sum.bUnversioned)
{
int32 Zero = 0;
Ar << Zero; // LegacyUE3version
Ar << Zero; // VersionUE4
Ar << Zero; // VersionLicenseeUE4
FCustomVersionContainer NoCustomVersions;
NoCustomVersions.Serialize(Ar);
}
else
{
// Must write out the last UE3 engine version, so that older versions identify it as new
int32 LegacyUE3Version = 864;
Ar << LegacyUE3Version;
Ar << Sum.FileVersionUE4;
Ar << Sum.FileVersionLicenseeUE4;
// Serialise custom version map.
Sum.CustomVersionContainer.Serialize(Ar);
}
}
Ar << Sum.TotalHeaderSize;
Ar << Sum.FolderName;
Ar << Sum.PackageFlags;
#if WITH_EDITOR
if (Ar.IsLoading())
{
// This flag should never be saved and its reused, so we need to make sure it hasn't been loaded.
Sum.PackageFlags &= ~PKG_NewlyCreated;
}
#endif // WITH_EDITOR
if( Sum.PackageFlags & PKG_FilterEditorOnly )
{
Ar.SetFilterEditorOnly(true);
}
Ar << Sum.NameCount << Sum.NameOffset;
if (Sum.FileVersionUE4 >= VER_UE4_SERIALIZE_TEXT_IN_PACKAGES)
{
Ar << Sum.GatherableTextDataCount << Sum.GatherableTextDataOffset;
}
Ar << Sum.ExportCount << Sum.ExportOffset;
Ar << Sum.ImportCount << Sum.ImportOffset;
Ar << Sum.DependsOffset;
if (Ar.IsLoading() && (Sum.FileVersionUE4 < VER_UE4_OLDEST_LOADABLE_PACKAGE || Sum.FileVersionUE4 > GPackageFileUE4Version))
{
return Ar; // we can't safely load more than this because the below was different in older files.
}
if (Ar.IsSaving() || Sum.FileVersionUE4 >= VER_UE4_ADD_STRING_ASSET_REFERENCES_MAP)
{
Ar << Sum.StringAssetReferencesCount << Sum.StringAssetReferencesOffset;
}
Ar << Sum.ThumbnailTableOffset;
int32 GenerationCount = Sum.Generations.Num();
Ar << Sum.Guid << GenerationCount;
if( Ar.IsLoading() && GenerationCount > 0 )
{
Sum.Generations.Empty( 1 );
Sum.Generations.AddUninitialized( GenerationCount );
}
for( int32 i=0; i= VER_UE4_ENGINE_VERSION_OBJECT )
{
if(Ar.IsCooking() || (Ar.IsSaving() && !FEngineVersion::Current().HasChangelist()))
{
FEngineVersion EmptyEngineVersion;
Ar << EmptyEngineVersion;
}
else
{
Ar << Sum.SavedByEngineVersion;
}
}
else
{
int32 EngineChangelist = 0;
Ar << EngineChangelist;
if(Ar.IsLoading() && EngineChangelist != 0)
{
Sum.SavedByEngineVersion.Set(4, 0, 0, EngineChangelist, TEXT(""));
}
}
if (Sum.GetFileVersionUE4() >= VER_UE4_PACKAGE_SUMMARY_HAS_COMPATIBLE_ENGINE_VERSION )
{
if(Ar.IsCooking() || (Ar.IsSaving() && !FEngineVersion::Current().HasChangelist()))
{
FEngineVersion EmptyEngineVersion;
Ar << EmptyEngineVersion;
}
else
{
Ar << Sum.CompatibleWithEngineVersion;
}
}
else
{
if (Ar.IsLoading())
{
Sum.CompatibleWithEngineVersion = Sum.SavedByEngineVersion;
}
}
Ar << Sum.CompressionFlags;
Ar << Sum.CompressedChunks;
Ar << Sum.PackageSource;
Ar << Sum.AdditionalPackagesToCook;
#if WITH_ENGINE
//@todo legacy
Ar << Sum.TextureAllocations;
#else
check(!"this can't serialize successfully");
#endif // WITH_ENGINE
Ar << Sum.AssetRegistryDataOffset;
Ar << Sum.BulkDataStartOffset;
if (Sum.GetFileVersionUE4() >= VER_UE4_WORLD_LEVEL_INFO)
{
Ar << Sum.WorldTileInfoDataOffset;
}
if (Sum.GetFileVersionUE4() >= VER_UE4_CHANGED_CHUNKID_TO_BE_AN_ARRAY_OF_CHUNKIDS)
{
Ar << Sum.ChunkIDs;
}
else if (Sum.GetFileVersionUE4() >= VER_UE4_ADDED_CHUNKID_TO_ASSETDATA_AND_UPACKAGE)
{
// handle conversion of single ChunkID to an array of ChunkIDs
if (Ar.IsLoading())
{
int ChunkID = -1;
Ar << ChunkID;
// don't load <0 entries since an empty array represents the same thing now
if (ChunkID >= 0)
{
Sum.ChunkIDs.Add( ChunkID );
}
}
}
}
return Ar;
}