Does someone know where I could learn more about it?
No idea. Would like to know too.
And does it change anything to the process when dynamically mounting a pak file?
On my project, the Packaged 4.15 version crashes when trying to mount 4.14 paks.
The opposite is also true. 4.14 project crashes mounting 4.15 pak.
Packaging on 4.15, adding the .uexp and .ubulk files to the pak, mounts successfully on 4.15 project.
So, I think we can say that 4.14 built projects will only mount 4.14- paks. And 4.15 built projects will only mount 4.15 paks. Haven’t tested 4.16 yet.
A difference on mounting though
I had a bug that after mounting the pak files, local (built with the project) skeletal meshes and some other assets would not be found anymore.
My code was Create FileManager for Paks; Load pak file; Mount; Load assets
To fix it, I had to store the standard FileManager before creating a new one and then, after loading the mounted assets, set the previous FileManager as the current one again.
Working code:
// 4.15 IWYU includes
#include "HAL/PlatformFilemanager.h"
#include "HAL/FileManager.h"
#include "Engine/StreamableManager.h"
#include "AssetRegistryModule.h"
#include "IPlatformFilePak.h"
void UUtils::LoadAssetsFromPakFolder(
const FString &DLCFolder,
TArray<TAssetPtr<UObject> > &Assets,
TArray<UClass*> &Classes)
{
UE_LOG(PaksLoadingLog, Log, TEXT("============= START LOADING PAKS ============="));
UE_LOG(PaksLoadingLog, Log, TEXT("\n\n\tGameContentDir: %s\n\tEngineContentDir: %s"),
*FPaths::GameContentDir(),
*FPaths::EngineContentDir());
FString Folder = DLCFolder;
FPaths::NormalizeDirectoryName(Folder);
TArray<FString> FileNames;
IPlatformFile* PreviousPlatformFile = NULL;
FPakPlatformFile *PlatformFile = NULL;
if (FString(FPlatformFileManager::Get().GetPlatformFile().GetName()).Equals(FString(TEXT("PakFile"))))
{
PlatformFile = static_cast<FPakPlatformFile*>(&FPlatformFileManager::Get().GetPlatformFile());
}
else
{
PlatformFile = new FPakPlatformFile;
if (!PlatformFile->Initialize(&FPlatformFileManager::Get().GetPlatformFile(), TEXT("")))
{
UE_LOG(PaksLoadingLog, Error, TEXT("FPakPlatformFile failed to initialize"));
return;
}
PreviousPlatformFile = &FPlatformFileManager::Get().GetPlatformFile();
FPlatformFileManager::Get().SetPlatformFile(*PlatformFile);
}
if (!PlatformFile->DirectoryExists(*Folder))
{
UE_LOG(PaksLoadingLog, Error, TEXT("Directory not found: %s"), *Folder);
return;
}
UE_LOG(PaksLoadingLog, Log, TEXT("DLC directory found: %s"), *Folder);
// Get list of files in folder
IFileManager &FileManager = IFileManager::Get();
FileManager.FindFiles(FileNames, *Folder);
UE_LOG(PaksLoadingLog, Log, TEXT("Iterating files in DLC folder"));
FStreamableManager StreamableManager;
// Iterates on every file, mount the paks and load the assets found in them
for (FString &FileName : FileNames)
{
// Check if extension match required format
if (FileName.Right(3).Equals(TEXT("pak"), ESearchCase::IgnoreCase))
{
UE_LOG(PaksLoadingLog, Log, TEXT("Pak file found: %s"), *FileName);
FString PakFullPath( FPaths::Combine(*Folder, *FileName) );
FString PakName, PakExtension;
FileName.Split( TEXT("."), &PakName, &PakExtension );
FString PakMountPoint = FPaths::Combine(FPaths::GameContentDir(), TEXT("DLC"), PakName);
if ( !PlatformFile->Mount(*PakFullPath, 0, *PakMountPoint) )
{
UE_LOG(PaksLoadingLog, Log, TEXT("Failed to mount %s"), *FileName);
continue;
}
FString VirtualMountPoint(FString::Printf(TEXT("/Game/DLC/%s/"), *PakName));
FPackageName::RegisterMountPoint(VirtualMountPoint, PakMountPoint);
UE_LOG(PaksLoadingLog, Log, TEXT("Mount successful. Iterating assets in %s"), *VirtualMountPoint);
// Print files in pak
struct FilesDump : public IPlatformFile::FDirectoryVisitor
{
FString mp_PakName;
TArray<FString> Files;
FilesDump(FString &PakName)
: mp_PakName(PakName)
{}
virtual bool Visit(const TCHAR *FilenameOrDirectory, bool bIsDirectory)
{
if (bIsDirectory)
{
UE_LOG(PaksLoadingLog, Log, TEXT("Found DIR in %s: %s"), *mp_PakName, FilenameOrDirectory);
}
else
{
const FString Filename(FilenameOrDirectory);
if (Filename.Contains(TEXT(".umap")) || Filename.Contains(TEXT(".uasset")))
{
Files.Add(FilenameOrDirectory);
UE_LOG(PaksLoadingLog, Log, TEXT("Found FILE in %s: %s"), *mp_PakName, FilenameOrDirectory);
}
}
return true;
}
};
FilesDump Visitor(FileName);
PlatformFile->IterateDirectoryRecursively(*PakMountPoint, Visitor);
for (const FString &AssetFilePath : Visitor.Files)
{
UE_LOG(PaksLoadingLog, Log, TEXT("Asset File: %s"), *AssetFilePath);
FString AssetFileName = FPackageName::GetShortName(AssetFilePath);
FString AssetName, AssetExtension;
// Get asset name and extension
AssetFileName.Split(TEXT("."), &AssetName, &AssetExtension);
// Replace system path for virtual path and extension for asset name
FString AssetRefPath(AssetFilePath.Replace(*PakMountPoint, *VirtualMountPoint));
AssetRefPath = AssetRefPath.Replace(*AssetExtension, *AssetName);
FPaths::RemoveDuplicateSlashes(AssetRefPath);
// If asset is a Blueprint
if (AssetName.StartsWith(TEXT("BP_")))
{
// Add class postfix
AssetRefPath += TEXT("_C");
UObject* LoadedClass = StreamableManager.SynchronousLoad(FStringAssetReference(AssetRefPath));
if (LoadedClass == NULL)
{
UE_LOG(PaksLoadingLog, Error, TEXT("Failed to load class: %s"), *AssetRefPath);
continue;
}
UBlueprintGeneratedClass* BPClass = Cast<UBlueprintGeneratedClass>(LoadedClass);
if (BPClass == NULL)
{
UE_LOG(PaksLoadingLog, Error, TEXT("Casting to BlueprintGeneratedClass failed: %s"), *AssetRefPath);
continue;
}
Classes.Add(BPClass);
}
else
{
Assets.Add(TAssetPtr<UObject>(FStringAssetReference(AssetRefPath)));
}
}
}
}
// return previous platform file manager to the top of the chain, so Unreal doesn't lose it's references
if (PreviousPlatformFile != NULL)
{
FPlatformFileManager::Get().SetPlatformFile(*PreviousPlatformFile);
}
UE_LOG(PaksLoadingLog, Log, TEXT("============= END LOADING PAKS ============="));
}