【UE4】 Pak解密、挂载、加载

首先,需要有一个打包好的PAK文件,可以参考我上一篇博客:UnrealPak打包
Editor下只能用未COOK的资源,打包后只能用COOK了的资源,否则加载不了
本例采用已加密的PAK文件,包含解密过程

简单讲解下几个知识点:
挂载(Mount):简单理解为保存资源的路径,在加载时就可以通过这个路径找到资源
加载(Load):将资源加载到内存,必须先挂载才能加载
挂载点(MountPoint):即将文件虚拟的挂载到某个路径上,在UE4中即为/Game可以直接读取到的位置
IPlatformFile:UE4的文件系统,创建时必须提供上一个IPlatformFile,呈链状
FPakPlatformFile:继承自IPlatformFile,用来挂载PAK
FPlatformFileManager:管理IPlatformFile的类,SetPlatformFile是设置最顶层的IPlatformFile
TSharedPtr<>:UE4封装的智能指针,这是是用于c++原生类的指针

直接上代码,在注释里进行详解
Build.cs添加PakFile模块

PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore"  , "PakFile"});

在自己的GameInstance.h添加如下代码:

//GameInstance.h
public:
	UYourGameInstance(const class FObjectInitializer&);
	virtual void Init(); //游戏启动时运行
	virtual void Shutdown(); //游戏关闭时运行
private:
	IPlatformFile* PlatformFile; //初始文件系统
	TSharedPtr<FPakPlatformFile> PakPlatformFile; //Pak文件系统

在自己的GameInstance.cpp添加如下代码:

UYourGameInstance::UYourGameInstance(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer) {
	//绑定PAK解密函数,如未加密可注释
	FCoreDelegates::GetPakEncryptionKeyDelegate().BindUObject(this, &UYourGameInstance::InitEncrypt);
	//保存初始文件系统指针
	PlatformFile = &FPlatformFileManager::Get().GetPlatformFile();
}
void UYourGameInstance::Init() {
	Super::Init();
	//新建PAK文件系统指针
	PakPlatformFile = MakeShareable(new FPakPlatformFile());
	//在PAK文件系统设置Parent文件系统,
	PakPlatformFile->Initialize(PlatformFile, TEXT(""));
	//将PakPlatformFile设置为最顶层,即首先查找的PakPlatformFile内的文件
	//再查找PakPlatformFile的Parent的文件,以此类推
	FPlatformFileManager::Get().SetPlatformFile(*PakPlatformFile);
	
	//这里的文件系统结构,PlatformFile是初始的,所以只有两层
	//PakPlatformFile --> PlatformFile 
}

void UYourGameInstance::Shutdown() {
	//恢复初始的文件结构
	if (PlatformFile) {
		FPlatformFileManager::Get().SetPlatformFile(*PlatformFile);
	}
	
	//这里的文件系统结构,PakPlatformFile 从链表中移除,里面挂载的文件将找不到
	//PlatformFile 
}

//挂载函数
//PakName : PAK包的路径,相对路径或绝对路径皆可
//MountPoint : 挂载点的位置,编辑器下相对或者绝对路径皆可,打包后切记一定使用相对路径,用于兼容各种文件加载
void UYourGameInstance::MountPakWithMountPoint(FString PakName, FString MountPoint) {
	if (FPaths::FileExists(PakName) && PakPlatformFile.IsValid()) {
		//获得已挂载的PAK文件,避免重复挂载
		TArray<FString> ExistPaks;
		PakPlatformFile->GetMountedPakFilenames(ExistPaks);
		if (ExistPaks.Find(PakName) >= 0) {
			return;
		}
		//将PAK挂载到MountPoint
		if (PakPlatformFile->Mount(*PakName, 1, *MountPoint)) {
			GEngine->AddOnScreenDebugMessage(INDEX_NONE, 60.f, FColor::Red, *FString::Printf(TEXT("Mount OK %s %s"), *PakName, *MountPoint));
		}
		else {
			GEngine->AddOnScreenDebugMessage(INDEX_NONE, 60.f, FColor::Red, *FString::Printf(TEXT("Mount File %s %s"), *PakName, *MountPoint));
		}
		//到这里其实已经完成了,后续就可以正常使用PAK包里的资源,用于项目中挂载和加载最好分开,一定要理解这个概念
		//但是第一次写的同学可能不太明白,下面写一些是测试代码用于加载和调试
		{
			//获得刚才挂载的PAK内所有文件路径
			TArray<FString> FileList;
			Pak->FindFilesAtPath(FileList, *Pak->GetMountPoint(), true, false, true);
			//从PAK包内静态加载StaticMesh
			for (FString& FileName : FileList) {
				if (FileName.EndsWith(TEXT(".uasset"))) {
					FString NewPath = FileName;
					NewPath.RemoveFromEnd(TEXT(".uasset"));
					FString ShortName = FPackageName::GetShortName(NewPath);
					int32 Pos = NewPath.Find("/Content/");
					NewPath = NewPath.RightChop(Pos + 8);
					NewPath = "/Game" + NewPath + "." + ShortName;
					//比如资源路径为 ../../../Content/Test/YourMesh.uasset,
					//挂载点为 FPaths::ProjectContentDir()
					//转换为 /Game/Test/YourMesh.YourMesh 进行加载
					UObject* LoadObj = StaticLoadObject(UObject::StaticClass(), NULL, *NewPath);
					if (LoadObj) {
						UStaticMesh* Mesh = Cast<UStaticMesh>(LoadObj);
						if (Mesh) {
							if (GEngine && GEngine->GameViewport && GEngine->GameViewport->GetWorld()) {
								UWorld* CurWorld = GEngine->GameViewport->GetWorld();
								AStaticMeshActor* MeshActor = CurWorld->SpawnActor<AStaticMeshActor>(AStaticMeshActor::StaticClass(), FVector(0, 0, 0), FRotator(0, 0, 0));
								if (MeshActor && MeshActor->GetStaticMeshComponent()) {
									//设置这个StaticMesh为可移动的
									MeshActor->GetStaticMeshComponent()->SetMobility(EComponentMobility::Movable);
									MeshActor->GetStaticMeshComponent()->SetStaticMesh(RealMesh);
									GEngine->AddOnScreenDebugMessage(INDEX_NONE, 60.f, FColor::White, MeshActor->GetName());
								}
							}
						}
					}
				}
			}
		}
	}
}

//解密函数,没啥好说的,就是用Base64转换下
void UYourGameInstance::InitEncrypt(uint8* Key) {
	FString KeyStr = TEXT("kDIta832HPHzaP+M8bDKrTGxnlU8QGYcEAWN5AAiLYI=");
	TArray<uint8> KeyBase64Ary;
	FBase64::Decode(KeyStr, KeyBase64Ary);
	char* KeyU8 = TCHAR_TO_UTF8(*KeyStr);
	FMemory::Memcpy(Key, KeyBase64Ary.GetData(), FAES::FAESKey::KeySize);
}

关于各资源间的引用关系各位可以查查其他文档,我没有细测
如果是用UE4播放PAK中的多媒体资源,如 .mp4,
有些额外的内容需要注意,非常恶心,
MediaPlayer自身会对路径进行一个转换(恶心恶心),可能会导致从PakPlatformFile中查不到资源,此类资源打包后一定要用相对路径,MountPoint和播放时填写的路径要一致,这块是直接字符串比较的,
比如你使用的MountPoint是相对路径,那么播放时填的路径也要相对路径

评论 19
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值